ASP Web Api - IoC - 解析HttpRequestMessage

时间:2012-10-19 15:39:52

标签: c# asp.net-web-api inversion-of-control castle-windsor hyprlinkr

我正在尝试使用ASP.NET WebAPI设置Castle Windsor。

我也在使用Hyprlinkr包(https://github.com/ploeh/Hyprlinkr),所以需要将一个HttpRequestMessage实例注入到我的控制器的一个依赖项中。

我正在关注Mark Seemann的这篇文章 - http://blog.ploeh.dk/2012/04/19/WiringHttpControllerContextWithCastleWindsor.aspx,但我发现虽然API运行,但当我调用它时,请求就会挂起。没有错误消息。就好像它处于无限循环中一样。它停留在我的Custom ControllerActivator中对Resolve的调用

我想我的一些城堡注册错了。如果我删除上面文章中提到的那些,那么我可以成功调用API(虽然没有我需要解决的依赖)

有什么想法吗?

代码低于

//Global.asax
public class WebApiApplication : HttpApplication
{
    private readonly IWindsorContainer container;

    public WebApiApplication()
    {
        container = 
            new WindsorContainer(
                new DefaultKernel(
                    new InlineDependenciesPropagatingDependencyResolver(), 
                    new DefaultProxyFactory()), 
                new DefaultComponentInstaller());

        container.Install(new DependencyInstaller());
    }

    protected void Application_Start()
    {        
        GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new WindsorCompositionRoot(this.container));
    }

// installer
public class DependencyInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.AddFacility<TypedFactoryFacility>();

        container.Register(
            Component.For<ValuesController>()
                .Named("ValuesController")
                .LifeStyle.PerWebRequest,

            Component.For<IResourceLinker>()
                .ImplementedBy<RouteLinker>()
                .LifeStyle.PerWebRequest,

            Component.For<IResourceModelBuilder>()
                .ImplementedBy<ResourceModelBuilder>()
                .LifeStyle.PerWebRequest,

                Component.For<HttpRequestMessage>()
                .Named("HttpRequestMessage")
                .LifeStyle.PerWebRequest
            );
    }
}

//Activator

public class WindsorCompositionRoot : IHttpControllerActivator
{
    private readonly IWindsorContainer container;

    public WindsorCompositionRoot(IWindsorContainer container)
    {
        this.container = container;
    }

    public IHttpController Create(
        HttpRequestMessage request,
        HttpControllerDescriptor controllerDescriptor,
        Type controllerType)
    {
        var controller = (IHttpController)this.container.Resolve(controllerType, new { request = request });

        request.RegisterForDispose(
            new Release(
                () => this.container.Release(controller)));

        return controller;
    }

// DependencyResolver   
public class InlineDependenciesPropagatingDependencyResolver : DefaultDependencyResolver
{
    protected override CreationContext RebuildContextForParameter(CreationContext current, Type parameterType)
    {
        if (parameterType.ContainsGenericParameters)
        {
            return current;
        }

        return new CreationContext(parameterType, current, true);
    }
}

EDIT *********** 附加信息****************

所以我设置了一个场景,控制器只将HttpRequestMessage作为ctor参数发现:

这有效:

//controller
public class ValuesController : ApiController
    {
        private readonly HttpRequestMessage _httpReq;

        public ValuesController(HttpRequestMessage httpReq)
        {
            _httpReq = httpReq;
        }
//IHttpControllerActivator
public IHttpController Create(
            HttpRequestMessage httpRequest,
            HttpControllerDescriptor controllerDescriptor,
            Type controllerType)
        {

            var controller = (IHttpController)this.container.Resolve(
                controllerType, new { httpReq = httpRequest });

            return controller;

然而,这不是。

//controller
public class ValuesController : ApiController
    {
        private readonly HttpRequestMessage _httpReq;

        public ValuesController(HttpRequestMessage request)
        {
            _httpReq = request;
        }

//IHttpControllerActivator
public IHttpController Create(
            HttpRequestMessage request,
            HttpControllerDescriptor controllerDescriptor,
            Type controllerType)
        {

            var controller = (IHttpController)this.container.Resolve(
                controllerType, new { request = request });

            return controller;

即。当anon对象具有名为“request”的属性并且控制器ctor arg被称为“request”时。它以某种方式使控制器认为它的请求属性为空。导致我看到错误的原因是什么:

  

无法重用'ApiController'实例。 'ApiController'必须是   每个传入消息构造。检查您的自定义   'IHttpControllerActivator'并确保它不会制造   相同的实例。

     

at System.Web.Http.ApiController.ExecuteAsync(HttpControllerContext)   controllerContext,CancellationToken cancellationToken)at   System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncInternal(HttpRequestMessage   请求,取消语音取消语言)   System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsync(HttpRequestMessage   请求,取消语音取消语言)

读一读 How can I enrich object composition in StructureMap without invoking setter injection?

它解释了类似的情况。

当然,hyprlinkr有一个名为“request”的HttpRequestMessage的ctor arg,所以我需要用该属性名指定anon对象。

有什么想法吗?

2 个答案:

答案 0 :(得分:12)

这是一个适合我的作曲根:

public class WindsorCompositionRoot : IHttpControllerActivator
{
    private readonly IWindsorContainer container;

    public WindsorCompositionRoot(IWindsorContainer container)
    {
        this.container = container;
    }

    public IHttpController Create(
        HttpRequestMessage request,
        HttpControllerDescriptor controllerDescriptor,
        Type controllerType)
    {
        var controller = (IHttpController)this.container.Resolve(
            controllerType,
            new
            {
                request = request
            });

        request.RegisterForDispose(
            new Release(
                () => this.container.Release(controller)));
        return controller;
    }

    private class Release : IDisposable
    {
        private readonly Action release;

        public Release(Action release)
        {
            this.release = release;
        }

        public void Dispose()
        {
            this.release();
        }
    }
}

以下是我创建容器的方法:

this.container =
    new WindsorContainer(
        new DefaultKernel(
            new InlineDependenciesPropagatingDependencyResolver(),
            new DefaultProxyFactory()),
        new DefaultComponentInstaller())
        .Install(new MyWindsorInstaller());

这里是InlineDependenciesPropagatingDependencyResolver:

public class InlineDependenciesPropagatingDependencyResolver : 
    DefaultDependencyResolver
{
    protected override CreationContext RebuildContextForParameter(
        CreationContext current,
        Type parameterType)
    {
        if (parameterType.ContainsGenericParameters)
        {
            return current;
        }

        return new CreationContext(parameterType, current, true);
    }
}

最后,这是我如何注册RouteLinker:

container.Register(Component
    .For<RouteLinker, IResourceLinker>()
    .LifestyleTransient());

需要注意的一点是,ApiController基类有一个名为HttpRequestMessage类型的Request的公共属性。正如my book的第10.4.3节所解释的那样,Windsor将尝试为每个可写属性分配一个值,如果它具有匹配的组件 - 并且该匹配不区分大小写。

当您将名为request的HttpRequestMessage传递给Resolve方法时,这正是发生的情况,因此您需要告诉Castle Windsor它应该放弃ApiControllers的Property Injection。以下是我在基于会议的注册中的表达方式:

container.Register(Classes
    .FromThisAssembly()
    .BasedOn<IHttpController>()
    .ConfigureFor<ApiController>(c => c.Properties(pi => false))
    .LifestyleTransient());

答案 1 :(得分:0)

为什么不在ASP.NET Web API中使用已经内置的机制 - 依赖性解析器

http://www.asp.net/web-api/overview/extensibility/using-the-web-api-dependency-resolver

在项目WebApiContrib中有CastleWindsor解析器实现,但正如我在Dependency解析器中看到的那样

https://github.com/WebApiContrib/WebApiContrib.IoC.CastleWindsor

正如Mark在评论中所说 - 实现IHttpControllerActivator的方法之一

http://blog.ploeh.dk/2012/10/03/DependencyInjectionInASPNETWebAPIWithCastleWindsor.aspx