Jos*_*nke 7 c# asp.net-mvc ninject httpcontext asp.net-web-api
我正在开发一个ASP.NET Web Api项目,并使其接受url中的版本信息.
例如:
- API/V1/myController的
- API/V2/myController的
现在我想在自定义LayoutRenderer中获取请求版本v1,v2Nlog.通常我会像下面的例子那样做.
[LayoutRenderer("Version")]
public class VersionLayoutRenderer : LayoutRenderer
{
protected override void Append(System.Text.StringBuilder builder, NLog.LogEventInfo logEvent)
{
var version = HttpContext.Current.Request.RequestContext.RouteData.Values["Version"];
builder.Append(version);
}
}
Run Code Online (Sandbox Code Playgroud)
问题: HttpContext.Current是NULL
我相信这是因为我使用异步包装为NLog与记录仪前几个电话也是Async.
在Ninject.Extensions.WebApi.UsageLogger中将记录器称为Async的示例.此时,HttpRequestMessage我们需要获取版本所需的所有信息.
/// <summary>
/// Initializes a new instance of the <see cref="UsageHandler" /> class.
/// </summary>
public UsageHandler()
{
var kernel = new StandardKernel();
var logfactory = kernel.Get<ILoggerFactory>();
this.Log = logfactory.GetCurrentClassLogger();
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var startTime = DateTime.Now;
// Log request
await request.Content.ReadAsStringAsync().ContinueWith(c =>
{
this.Log.Info("{0}: {1} called from {2}", request.Method, HttpUtility.UrlDecode(request.RequestUri.AbsoluteUri), ((HttpContextBase)request.Properties["MS_HttpContext"]).Request.UserHostAddress);
this.Log.Info("Content-Type: {0}, Content-Length: {1}", request.Content.Headers.ContentType != null ? request.Content.Headers.ContentType.MediaType : string.Empty, request.Content.Headers.ContentLength);
this.Log.Info("Accept-Encoding: {0}, Accept-Charset: {1}, Accept-Language: {2}", request.Headers.AcceptEncoding, request.Headers.AcceptCharset, request.Headers.AcceptLanguage);
if (!string.IsNullOrEmpty(c.Result))
{
if (this.MaxContentLength > 0 && c.Result.Length > this.MaxContentLength)
{
this.Log.Info("Data: {0}", HttpUtility.UrlDecode(c.Result).Substring(0, this.MaxContentLength - 1));
}
else
{
this.Log.Info("Data: {0}", HttpUtility.UrlDecode(c.Result));
}
}
});
var response = await base.SendAsync(request, cancellationToken);
// Log the error if it returned an error
if (!response.IsSuccessStatusCode)
{
this.Log.Error(response.Content.ReadAsStringAsync().Result);
}
// Log performance
this.Log.Info("Request processing time: " + DateTime.Now.Subtract(startTime).TotalSeconds + "s");
return response;
}
Run Code Online (Sandbox Code Playgroud)
问题
什么VersionLayoutRenderer是以通用方式进行工作的最佳方式?我可以添加MessageHandler并将HttpRequest绑定到某些异步范围吗?如果是这样,任何指导方针都会受到重视,因为我仍然习惯了Ninject.
暂时我将版本信息直接添加到UsageHandler中的日志调用,但我真的想要一个更通用的解决方案,我总是可以依赖日志记录中的版本信息.
编辑:将问题更新为更具体,并包含更多详细信息.
实际问题对于您应该如何使用 Ninject 来说实际上是中立的 - 您只需要确定处理的阶段性,以便任何将要异步运行的对象都拥有它们所需的一切,而无需依赖魔法HttpContext.Current。首先在没有 DI 容器的情况下使用它。
然后,使用 Ninject 的主要步骤是:-
您的Bind语句需要运行一次。请参阅 Ninject.MV3 wiki 了解最佳方法(在合并之前,基于 NuGet 的版本不存在 OOTB)
正如 @rickythefox (+1'd) 所说,您的注册应该将线程/上下文相关数据烘焙到对象中,并且您配置注册,以便它可以在请求处理的早期发生,当您仍在线程上时HttpContext.Current
kernel.Bind<ILogger>()
// TODO replace GCCL with something like GetClassLogger(ctx.Request.Service.ReflectedType) - see the wiki for examples
.ToMethod( ctx=> ctx.Get<ILoggerFactory>().GetCurrentClassLogger())
.InRequestScope()
.WithConstructorArgument("context",c=>HttpContext.Current);
Run Code Online (Sandbox Code Playgroud)然后只需让处理程序的构造函数采用 a ILogger,即可将其分配给.Log(我希望不是static:D)
注意,我们的目标是让你永远不要写kernel.Get()句号。
但这里真正的问题是,正确使用 WebApi 并不涉及使用HttpContext.Current或任何其他魔术static方法或任何类似的方法(为了可测试性,使自己独立于托管上下文(自托管、OWIN 等),以及更多原因)。
另外,如果您使用 NLog(或 Log4Net),您还应该查看Ninject.Extensions.Logging包(和源代码)。