7

Używam Windsor do zarządzania IoC dla moich kontrolerów w projekcie WebAPI. Mam DependencyResolver ładnie działa, aby rozwiązać zależności kontrolera, ale teraz szukam wstrzyknąć zależności do niestandardowego filtra akcji używam do zarządzania uwierzytelniania.W jaki sposób wykonać zastrzyk zależności na filtry akcji w ASP.NET 4 RC WebAPI?

Rozejrzałem się za pomocą niestandardowego ActionInvoker, ale nie jest jasne z interfejsu, że WebAPI używa sposobu, w jaki powinienem rozpatrzyć zależności właściwości w atrybucie filtra działania niestandardowego przed jego wykonaniem. Ktoś ma dobry przykład, jak to zrobić w MVC 4 RC?

EDIT: Jestem świadomy, że nie może zrobić zastrzyk konstruktora na filtrach, bo są atrybuty, a zatem wystąpienia przez framework .NET - ale mam nadzieję, że jest jakiś punkt w cyklu realizacji, które dzieje się po filtr jest tworzony, ale PRZED jego wykonaniem, gdzie mógłbym uruchomić niestandardowy kod, aby wyliczyć we właściwościach publicznych filtrów i wstrzyknąć niezbędne usługi.

+0

IMHO bardzo dobra wersja odsprzężona jest opisana w tym [pytanie (i odpowiedź) - ASP.NET MVC IFilterProvider i rozdział obaw] (http://stackoverflow.com/questions/10708565/asp-net-mvc-ifilterprovider- i - separacja obaw). –

Odpowiedz

10

Filtry akcji są atrybutami. W atrybucie .NET proces tworzenia instancji jest zarządzany przez środowisko wykonawcze .NET i nie masz nad nim kontroli. Tak więc jedną z możliwości jest użycie Poor Man's Dependency Injection, które osobiście doradziłbym ci.

Inną możliwością jest użycie atrybutu układów:

public class MyActionFilterAttribute : Attribute 
{ 

} 

i wtedy filtr działania poprzez wtryskiwanie konstruktor

public class MyActionFilter : ActionFilterAttribute 
{ 
    private readonly IFoo _foo; 
    public MyActionFilter(IFoo foo) 
    { 
     _foo = foo; 
    } 

    public override void OnActionExecuting(HttpActionContext actionContext) 
    { 
     if (actionContext.ActionDescriptor.GetCustomAttributes<MyActionFilterAttribute>().Any()) 
     { 
      // The action is decorated with the marker attribute => 
      // do something with _foo 
     } 
    } 
} 

po czym zarejestrować jako globalny filtr działania w Application_Start:

IFoo foo = .... 
GlobalConfiguration.Configuration.Filters.Add(new MyActionFilter(foo)); 
+0

Darin - dzięki za to; Próbowałem już podejścia do lokalizatora usług, ale szukam czegoś trochę czystszego - zobacz edycję mojego pytania, które mam nadzieję wyjaśni, czego szukam. –

+0

@DylanBeattie, nie, jeśli chcesz użyć konstruktora wtrysku (który jest właściwym sposobem wstrzykiwania wymaganych zależności do klas), musisz mieć kontrolę nad tworzeniem klasy, czego niestety nie masz w przypadku atrybutów. Dlatego możesz użyć interfejsu znacznika, jak pokazano w mojej odpowiedzi. –

+0

jeśli zarejestrujesz filtry w swoim kontenerze i utworzysz kontener w pliku global.asax, możesz użyć kontenera, aby rozwiązać filtry, np. _container.ResolveAll (t) .Cast () .ForEach (GlobalConfiguration.Configuration.Filters.Add) –

4

Miałem ten sam problem, ale postanowiłem pójść R ServiceLocator (DependencyResolver.GetService) o tym, jak jej w ramy wydaje mi się być poprawnym podejściem

public class RequiresSessionAttribute : 
    ActionFilterAttribute 
{ 
    public override void OnActionExecuting(HttpActionContext actionContext) 
    { 
     var sessionService = 
      (ISessionService) actionContext 
        .ControllerContext.Configuration.DependencyResolver 
        .GetService(typeof (ISessionService)); 

     var sessionId = HttpUtility 
      .ParseQueryString(actionContext.Request.RequestUri.Query) 
      .Get("sessionId"); 

     if (sessionId == null 
      || !sessionService.IsValid(sessionId)) 
      throw new SessionException(); 

     base.OnActionExecuting(actionContext); 
    } 
} 

i tutaj jest test dla tego atrybutu, trochę bólu, ale możliwe

public class requires_sessionId 
{ 
    [Fact] 
    void can_call_action_with_session_id() 
    { 
     var context = GetContext("http://example.com/?sessionId=blaa"); 

     var sut = new RequiresSessionAttribute(); 

     Assert.DoesNotThrow(
      () => sut.OnActionExecuting(context)); 
    } 

    [Fact] 
    void can_not_call_action_without_session_id() 
    { 
     var context = GetContext("http://example.com/"); 

     var sut = new RequiresSessionAttribute(); 

     Assert.Throws<SessionException>(
      () => sut.OnActionExecuting(context)); 
    } 

    HttpActionContext GetContext(string url) 
    { 
     var sessionServiceMock = new Mock<ISessionService>(); 
     sessionServiceMock 
      .Setup(x => x.IsValid(It.IsAny<string>())) 
      .Returns(true); 

     var dependancyResolverMock = new Mock<IDependencyResolver>(); 
     dependancyResolverMock 
      .Setup(x => x.GetService(It.IsAny<Type>())) 
      .Returns(sessionServiceMock.Object); 

     var config = new HttpConfiguration 
       { 
        DependencyResolver = dependancyResolverMock.Object 
       }; 
     var controllerContext = new HttpControllerContext 
       { 
        Configuration = config, 
        Request = new HttpRequestMessage(
           HttpMethod.Get, 
           url) 
       }; 

     return 
      new HttpActionContext 
       { 
        ControllerContext = controllerContext, 
       }; 
    } 
} 
+1

To nie jest uważany za dobry sposób, nie może wyśmiewać –

+0

jego nie jest łatwe, ale można –

+0

Może nie najlepszy sposób, ale w niektórych przypadkach może to być jedyny sposób. –