如何在ASP.NET 4 RC WebAPI中将依赖项注入到操作过滤器中?

Dyl*_*tie 7 castle-windsor action-filter asp.net-mvc-4 asp.net-web-api

我正在使用Windsor在WebAPI项目中为我的控制器管理IoC.我有一个很好地解决控制器依赖的DependencyResolver,但现在我想将依赖注入我用来管理身份验证的自定义动作过滤器.

我已经研究过使用自定义的ActionInvoker,但是从界面上看,WebAPI正在使用如何在执行之前解析自定义动作过滤器属性的属性依赖性并不清楚.任何人都有一个很好的例子,说明如何在MVC 4 RC中做到这一点?

编辑:我知道你不能对过滤器进行构造函数注入,因为它们是属性,因此由.NET框架实例化 - 但我希望执行生命周期中的某些点在过滤器实例化之后发生但是在执行之前,我可以运行一些自定义代码来枚举过滤器的公共属性并注入必要的服务.

Dar*_*rov 10

动作过滤器是属性.在.NET属性中,实例化过程由.NET运行时管理,您无法控制它.所以一种可能性是使用穷人的依赖注入,我个人建议你反对.

另一种可能性是使用标记属性:

public class MyActionFilterAttribute : Attribute 
{ 

}
Run Code Online (Sandbox Code Playgroud)

然后使用构造函数注入动作过滤器:

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后将其注册为全局操作过滤器Application_Start:

IFoo foo = ....
GlobalConfiguration.Configuration.Filters.Add(new MyActionFilter(foo));
Run Code Online (Sandbox Code Playgroud)


Ant*_*ton 5

我遇到了同样的问题,但决定为此使用 ServiceLocator (DependencyResolver.GetService),因为它在框架中在我看来是一种有效的方法

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是这个属性的测试,有点痛苦但可能

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,
                };
    }
}
Run Code Online (Sandbox Code Playgroud)