我正在开发一个ASP.NET Web Api项目,并使其接受url中的版本信息。
例如:
- api / v1 / MyController
- api / v2 / MyController
现在,我想在Nlog
的自定义LayoutRenderer中获取请求版本 v1,v2 。通常我会像下面的例子那样做。
[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);
}
}
问题: HttpContext.Current
为空
我认为这是因为我对NLog
使用了Async wrappers,而在记录器之前的一些调用也是Async
。
在Ninject.Extensions.WebApi.UsageLogger中称为异步的记录器示例。此时,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;
}
问题
以{strong>通用方式使VersionLayoutRenderer
工作的最佳方法是什么?我可以添加MessageHandler并将HttpRequest绑定到某些异步范围吗?如果是这样,任何指导方针都会受到重视,因为我仍然习惯Ninject
。
暂时我将版本信息直接添加到UsageHandler中的日志调用中,但我真的想要一个更通用的解决方案,我总是可以依赖日志记录中的版本信息。
修改:将问题更新为更具体,并包含更多详细信息。
答案 0 :(得分:2)
尝试使用以下内容注入上下文:
kernel.Bind<IDependency>()
.To<Mydependency>()
.InRequestScope()
.WithConstructorArgument("context",c=>HttpContext.Current);
答案 1 :(得分:1)
实际问题是非常中立的,你应该用Ninject做什么 - 你只需要对你的处理进行分阶段,这样任何正在运行异步的对象都拥有他们需要的一切,而不依赖于魔法{{1} }。首先使用没有DI容器的工作。
然后,要使用Ninject,主要步骤是: -
您的HttpContext.Current
语句需要运行一次。请参阅Ninject.MV3 wiki了解最佳方法(直到它合并为止,没有OOTB与基于NuGet的版本)
正如@rickythefox(+ 1'd)所说,您的注册应该将线程/上下文相关数据烘焙到对象中并配置注册,以便在请求处理的早期发生,当您仍然在Bind
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);
,可以将其分配给ILogger
(我希望不是.Log
:D)
注意,目的是永远不要写static
,期间。
这里真正的问题是,正确使用WebApi不涉及使用kernel.Get()
或任何其他魔法HttpContext.Current
方法或类似的东西(为了可测试性,使自己独立于托管上下文(自托管,OWIN等),以及更多原因)。
此外,如果您使用的是NLog(或Log4Net),您还应该查看static
包(和来源)。
答案 2 :(得分:0)
GlobalConfiguration类可以让您访问路由配置。
// The code below assumes a map routing convention of api/{version}/{controller}/....
// This will give you the configured routes
var routes = GlobalConfiguration.Configuration.Routes;
// This will give you the route templates
var templates = routes
.Select(route => route.RouteTemplate);
// This will give you the distinct versions for all controllers
var versions = routes
.Select(route => route.RouteTemplate)
.Select(template => template.Split("/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
.Select(values => values[1])
.Distinct();
// This will give you the distinct versions for a controller with the specified name
var name = "MyController";
var controllerVersions = routes
.Select(route => route.RouteTemplate)
.Select(template => template.Split("/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
.Where(values => String.Equals(values[2], name, StringComparison.OrdinalIgnoreCase))
.Select(values => values[1])
.Distinct();
我不确定您是否尝试使用已知值(控制器的名称)解析版本,或者您是否尝试动态解析它。如果注入当前的HttpContext,则可以使用上下文的请求URI通过路由模板过滤路由。
编辑:在您发表评论后,我意识到路由配置不是您所追求的。
如果最终目标是在控制器中实现日志记录,您可能需要查看Tracing in ASP.NET Web API,因为支持内置到Web API基础结构的跟踪。