Castle windsor 如何将依赖项注入ASP.NET 4 RC WebAPI中的操作筛选器?

Castle windsor 如何将依赖项注入ASP.NET 4 RC WebAPI中的操作筛选器?,castle-windsor,asp.net-mvc-4,asp.net-web-api,action-filter,Castle Windsor,Asp.net Mvc 4,Asp.net Web Api,Action Filter,我正在使用Windsor管理WebAPI项目中控制器的IoC。我有一个DependencyResolver,可以很好地解决控制器依赖关系,但现在我希望将依赖关系注入到一个自定义操作过滤器中,我使用它来管理身份验证 我已经研究过如何使用自定义ActionInvoker,但从接口上看不清楚WebAPI使用的是什么,我将如何在自定义action filter属性执行之前解析属性依赖关系。有谁有一个在MVC4RC中如何做到这一点的好例子吗 编辑:我知道你不能在过滤器上进行构造函数注入,因为它们是属性,因

我正在使用Windsor管理WebAPI项目中控制器的IoC。我有一个DependencyResolver,可以很好地解决控制器依赖关系,但现在我希望将依赖关系注入到一个自定义操作过滤器中,我使用它来管理身份验证

我已经研究过如何使用自定义ActionInvoker,但从接口上看不清楚WebAPI使用的是什么,我将如何在自定义action filter属性执行之前解析属性依赖关系。有谁有一个在MVC4RC中如何做到这一点的好例子吗


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

操作过滤器是属性。在.NET属性中,实例化过程由.NET运行时管理,您无法控制它。所以有一种可能是我个人建议你不要使用的

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

public class MyActionFilterAttribute : Attribute 
{ 

}
然后使用构造函数注入进行操作筛选器:

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

我也有同样的问题,但决定使用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);
    }
}
这是对这个属性的一个测试,有点痛苦,但可能

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,
                };
    }
}
公共类需要会话ID
{
[事实]
void可以使用会话id()调用操作
{
var context=GetContext(“http://example.com/?sessionId=blaa");
var sut=新的RequiresSessionAttribute();
Assert.DoesNotThrow(
()=>sut.OnActionExecuting(上下文));
}
[事实]
没有会话id()无效不能调用操作
{
var context=GetContext(“http://example.com/");
var sut=新的RequiresSessionAttribute();
断言。抛出(
()=>sut.OnActionExecuting(上下文));
}
HttpActionContext获取上下文(字符串url)
{
var sessionServiceMock=new Mock();
会话服务锁
.Setup(x=>x.IsValid(It.IsAny()))
.返回(真);
var dependancResolverMock=new Mock();
依赖性旋转座舱
.Setup(x=>x.GetService(It.IsAny()))
.Returns(sessionServiceMock.Object);
var config=新的HttpConfiguration
{
DependencyResolver=DependencyResolverMock.Object
};
var controllerContext=新的HttpControllerContext
{
配置=配置,
请求=新的HttpRequestMessage(
HttpMethod.Get,
(网址)
};
返回
新HttpActionContext
{
ControllerContext=ControllerContext,
};
}
}

Darin-谢谢你的帮助;我已经尝试过使用服务定位器的方法,但我正在寻找更清晰的方法-请参阅“编辑我的问题”,希望它能澄清我所寻找的内容。@DylanBeattie,否,如果您想使用构造函数注入(这是将所需依赖项注入类的正确方法)您需要控制类的实例化,不幸的是,在属性的情况下,您没有这样做。这就是为什么您可以使用我的答案中所示的标记接口。如果您在容器中注册过滤器,并且您正在global.asax中创建容器,那么您可以使用容器解析过滤器,例如_container.ResolveAll(t.Cast().ForEach(GlobalConfiguration.Configuration.filters.Add)这是一个非常好的解耦版本。也许不是最好的方式,但在某些情况下,这可能是唯一的方式。
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,
                };
    }
}