7

Estoy usando Windsor para administrar IoC para mis controladores en un proyecto WebAPI. Tengo un DependencyResolver que funciona bien para resolver las dependencias del controlador, pero ahora estoy buscando inyectar dependencias en un filtro de acción personalizado que estoy usando para administrar la autenticación.¿Cómo puedo hacer la inyección de dependencia en filtros de acción en ASP.NET 4 RC WebAPI?

He investigado el uso de un ActionInvoker personalizado, pero no está claro desde la interfaz en que WebAPI está utilizando cómo resolvería las dependencias de propiedad en el atributo de filtro de acción personalizado antes de que se ejecute. ¿Alguien tiene un buen ejemplo de cómo hacer esto en el MVC 4 RC?

EDITAR: Soy consciente de que no se pueden inyectar constructores en filtros, porque son atributos y, por lo tanto, instanciados por .NET Framework, pero espero que haya algún punto en el ciclo de ejecución que ocurra DESPUÉS del filtro se crea una instancia, pero ANTES de que se ejecute, donde podría ejecutar algún código personalizado para enumerar a través de las propiedades públicas de los filtros e inyectar los servicios necesarios.

+0

En mi humilde opinión, la muy desacoplada versión se describe en esta [pregunta (y respuesta) - ASP.NET MVC IFilterProvider y separación de preocupaciones] (http://stackoverflow.com/questions/10708565/asp-net-mvc-ifilterprovider- y-separación-de-preocupaciones). –

Respuesta

10

Los filtros de acción son atributos. En el atributo .NET, el tiempo de ejecución de .NET gestiona el proceso de creación de instancias y no tiene control sobre él. Entonces, una posibilidad es usar Poor Man's Dependency Injection con lo que personalmente te aconsejaría que no lo hicieras.

Otra posibilidad es utilizar un atributo marcador:

public class MyActionFilterAttribute : Attribute 
{ 

} 

y luego tener el filtro de acción mediante la inyección de constructor:

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 
     } 
    } 
} 

y luego registrarlo como un filtro de acción global en Application_Start:

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

Darin - gracias por esto; Ya probé el enfoque del localizador de servicios, pero estoy buscando algo un poco más limpio: ver la edición de mi pregunta que, con suerte, aclara lo que estoy buscando. –

+0

@DylanBeattie, no, si desea usar la inyección de constructor (que es la forma correcta de insertar las dependencias requeridas en las clases) necesita tener control sobre la creación de instancias de la clase que desafortunadamente no tiene en el caso de los atributos. Es por eso que podrías usar la interfaz del marcador como se muestra en mi respuesta. –

+0

si registra los filtros con su contenedor y está creando el contenedor en el archivo global.asax, puede usar el contenedor para resolver los filtros, p. _container.ResolveAll (t) .Cast () .ForEach (GlobalConfiguration.Configuration.Filters.Add) –

4

Tuve el mismo problema, pero decidí ir por r la ServiceLocator (DependencyResolver.GetService) para esto, ya que está en el marco me parece ser un enfoque válido

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); 
    } 
} 

y aquí es una prueba para este atributo, un poco de dolor, pero es posible

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

No se considera una buena manera, no se puede burlar –

+0

no es fácil, pero puede –

+0

Quizás no sea la mejor manera, pero en algunos casos, podría ser la única forma. –

Cuestiones relacionadas