7

Uso Windsor per gestire IoC per i miei controller in un progetto WebAPI. Ho un DependencyResolver che funziona bene per risolvere le dipendenze del controller, ma ora sto cercando di iniettare le dipendenze in un filtro azioni personalizzato che sto usando per gestire l'autenticazione.Come posso eseguire l'integrazione delle dipendenze nei filtri di azione in Web Web di ASP.NET 4 RC?

Ho cercato di utilizzare un ActionInvoker personalizzato ma non è chiaro dall'interfaccia che WebAPI stia usando come risolvere le dipendenze delle proprietà sull'attributo del filtro azioni personalizzato prima dell'esecuzione. Qualcuno ha un buon esempio di come farlo in MVC 4 RC?

EDIT: Sono consapevole che non è possibile eseguire l'iniezione del costruttore sui filtri, perché sono attributi e pertanto istanziati dal framework .NET, ma spero che ci sia un punto nel ciclo di vita dell'esecuzione che si verifica dopo il il filtro viene istanziato ma PRIMA che venga eseguito, dove posso eseguire un codice personalizzato per enumerare le proprietà pubbliche dei filtri e iniettare i servizi necessari.

+0

IMHO la versione molto disaccoppiata è descritta in questa [domanda (e risposta) - ASP.NET MVC IFilterProvider e separazione dei problemi] (http://stackoverflow.com/questions/10708565/asp-net-mvc-ifilterprovider- e-separazione dei problemi). –

risposta

10

I filtri di azione sono attributi. Nell'attributo .NET il processo di creazione di istanze è gestito dal runtime .NET e non si ha il controllo su di esso. Quindi una possibilità è quella di usare Poor Man's Dependency Injection che personalmente consiglierei contro.

Un'altra possibilità è quella di utilizzare un attributo marcatore:

public class MyActionFilterAttribute : Attribute 
{ 

} 

e poi il filtro un'azione mediante iniezione del costruttore:

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

e quindi registrarlo come filtro un'azione globale in Application_Start:

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

Darin - grazie per questo; Ho già provato l'approccio al localizzatore di servizi, ma sto cercando qualcosa di un po 'più pulito - vedi modifica sulla mia domanda che, si spera, chiarisca cosa sto cercando. –

+0

@DylanBeattie, no, se si desidera utilizzare l'iniezione del costruttore (che è il modo corretto di inserire le dipendenze richieste nelle classi) è necessario avere il controllo sull'istanza della classe che, sfortunatamente, non si ha nel caso degli attributi. Ecco perché è possibile utilizzare l'interfaccia marker come mostrato nella mia risposta. –

+0

se si registrano i filtri con il contenitore e si sta creando il contenitore in global.asax, è possibile utilizzare il contenitore per risolvere i filtri, ad es. _container.ResolveAll (t) .Cast () .ForEach (GlobalConfiguration.Configuration.Filters.Add) –

4

Ho avuto lo stesso problema, ma ho deciso di andare per R il ServiceLocator (DependencyResolver.GetService) per questo, come nel quadro mi sembra di essere un valido approccio

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

ed ecco un banco di prova per questo attributo, po 'di dolore ma possibile

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

Non è considerato un buon modo, non può essere deriso –

+0

non è facile, ma è possibile –

+0

Forse non il modo migliore, ma in alcuni casi potrebbe essere l'unico modo. –

Problemi correlati