使用Ninject在Web API中捕获和注入HttpRequestMessage

时间:2014-01-23 15:08:59

标签: asp.net-web-api ninject

我有一个类需要访问我的Web API服务中的HttpRequestMessage。目前,我已经获得以下代码来捕获管道中的消息并将其保存以供日后使用(基于thisthis):

public class ContextCapturingControllerActivator : IHttpControllerActivator
{
    private readonly IKernel kernel;
    private HttpRequestMessage requestMessage;

    public ContextCapturingControllerActivator(IKernel kernel)
    {
        this.kernel = kernel;
    }

    public IHttpController Create(HttpRequestMessage requestMessage, 
                                  HttpControllerDescriptor controllerDescriptor, 
                                  Type controllerType)
    {
        this.kernel.Rebind<HttpRequestMessage>()
            .ToConstant<HttpRequestMessage>(requestMessage);

        var controller = (IHttpController)this.kernel.GetService(controllerType);

        this.requestMessage = requestMessage;
        requestMessage.RegisterForDispose(
            new Release(() => this.kernel.Release(controller)));

        return controller; 
    }

    private class Release : IDisposable
    {
        private readonly Action release;

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

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

在我的作品根目录中,我配置了ControllerActivator

kernel.Bind<IHttpControllerActivator>()
      .To<ContextCapturingControllerActivator>();

最终结果是,从配置的角度来看,HttpRequestMessage被“神奇地”注入到请求的任何地方,因为它是在ControllerActivator内为我们完成的。我无法从我的合成根注入消息。我也不会对Rebind感到疯狂,因为每次调用服务时都要避免添加新的绑定。我怀疑这是由于Web API堆栈的单例性质,但还是无法理清如何正确处理它。

一般情况下,由于报告错误(并忽略)here,我无法使用Ninject web api的最新不稳定Nuget包。

任何人都可以提出正确的方法来改进我的代码,使其更清晰,让未来的维护者更轻松(让我们面对它 - 这可能是我)。

感谢。

3 个答案:

答案 0 :(得分:1)

这就是我所做的,但我相信这取决于Web API 2.0 +。

我创建了一个包装当前上下文的http请求的实例类:

public class HttpRequestMessageWrapper
{
    private readonly HttpRequestMessage m_httpRequestMessage;

    public HttpRequestMessageWrapper()
    {
        m_httpRequestMessage = HttpContext.Current.Items["MS_HttpRequestMessage"] as HttpRequestMessage;
    }

    public HttpRequestMessage RequestMessage
    {
        get
        {
            return m_httpRequestMessage;
        }
    }
}

然后我在请求范围内使用ToMethod绑定将HttpRequestMessage绑定到属性。

container.Bind<HttpRequestMessage>().ToMethod(ctx => new HttpRequestMessageWrapper().RequestMessage).InRequestScope();

答案 1 :(得分:0)

我已经尝试过@Mackers提出的方法,这是最干净的方法....但是,在我的具体情况下,由于时间问题,它无法正常工作。对于我的情况,我需要将一个对象注入apicontroller ctor,该对象需要HttpRequestMessage。在构造和初始化控制器之前,不会填充HttpContext.Current.Items["MS_HttpRequestMessage"],我找不到任何其他方式来访问它。所以我使用了创建自定义DelegatingHandler并在它们进入时重新绑定当前请求消息。

public class CurrentHttpRequestMessageHandler : DelegatingHandler
    {
        [SecuritySafeCritical]
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            UpdateScopeWithHttpRequestMessage(request);

            return base.SendAsync(request, cancellationToken);
        }

        internal static void UpdateScopeWithHttpRequestMessage(HttpRequestMessage request)
        {
            NinjectConfig.GetConfiguredKernel().Rebind<HttpRequestMessage>().ToMethod(ctx => { return request; })
                .InRequestScope();
        }
    }

GetConfiguredKernel是我创建的一个静态方法,用于简单地返回已配置的静态内核实例。

 public class NinjectConfig
    {
        private static readonly Bootstrapper bootstrapper = new Bootstrapper();
        private static StandardKernel _kernel;

        public static void Start()
        {
            DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
            DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
            bootstrapper.Initialize(CreateKernel);
        }

        public static IKernel GetConfiguredKernel()
        {
            if (_kernel != null)
                return _kernel;

            return CreateKernel();
        }
        ....

然后使用HttpConfiguration注册DelegatingHandler:

config.MessageHandlers.Add(new CurrentHttpRequestMessageHandler());

答案 2 :(得分:0)

基于Macker的答案,System.Web具有HttpRequestBase class,您可以使用它并简化代码的单元测试。在需要该请求的代码中的任何位置,将HttpRequestBase类型指定为构造函数参数,并使用以下方法注册它:

注入示例:

this._settings[t]

团结示例:

select t1.item, t2.item, count(*) as num_customers
from transaction t1 join
     transaction t2
     on t1.consumer_id = t2.consumer_id and
        t1.item < t2.item
group by t1.item, t2.item