我有一个类需要访问我的Web API服务中的HttpRequestMessage。目前,我已经获得了以下代码来捕获管道中的消息并将其保存以供日后使用(基于this和this):
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包。
任何人都可以建议正确的方法来改进我的代码,使其更清晰,让未来的维护者生活更轻松(让我们面对它 - 这可能是我)。
谢谢。
这就是我所做的,但我相信这取决于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();
我已经尝试过@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());
基于Macker的答案,System.Web有一个HttpRequestBase class,您可以使用它并简化单元测试代码。在需要请求的代码中的任何位置,将HttpRequestBase类型指定为构造函数参数,并使用以下方法注册它:
Ninject示例:
Bind<HttpRequestBase>().ToMethod(context => new HttpRequestWrapper(HttpContext.Current.Request));
Unity示例:
container.RegisterType<HttpRequestBase>(new InjectionFactory(_ => new HttpRequestWrapper(HttpContext.Current.Request)));