请求参数外部控制器
我正在开发一个ASP.NET Web Api项目,并让它接受URL中的版本信息。
例如:
现在我想要在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
是NULL
我相信这是因为我使用NLog
Async包装器,并且在Logger之前还有一些调用也是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;
}
问题什么是使通用的方式使VersionLayoutRenderer
工作的最佳方式? 我可以添加MessageHandler并将HttpRequest绑定到某个异步作用域? 如果是的话,任何指导方针将不胜感激,因为我仍然习惯于Ninject
。
目前,我将版本信息直接添加到UsageHandler中的Log Call中,但我真的很喜欢更通用的解决方案,我可以始终依赖日志中的版本信息。
编辑:更新的问题更具体,包括更多的细节。
实际的问题对于Ninject应该怎么处理是非常中性的 - 你只需要获得处理的阶段性,以便任何正在运行异步的对象都拥有他们所需的所有内容,而不依赖于神奇的HttpContext.Current
。 先得到没有DI容器的工作。
然后,要使用Ninject,主要步骤是: -
您的Bind
语句需要运行一次。 请参阅Ninject.MV3 wiki以获得最佳方法(直到它被合并,但基于NuGet的版本没有OOTB)
正如@rickythefox(+ 1'd)所说,你的注册应该将线程/上下文相关数据烧入对象中,并且配置注册,以便在请求处理早期发生,当你仍然在HttpContext.Current
的线程上时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
(我希望它不是static
:D)
注意,目标是永远不要编写一个kernel.Get()
,永远,周期。
但真正的问题在于,正确使用WebApi并不涉及使用HttpContext.Current
或任何其他魔术static
方法或类似的东西(为了可测试性,使自己独立于主机上下文(自主主机,OWIN等)以及许多更多原因)。
另外,如果您使用的是NLog(或Log4Net),您还应该查看Ninject.Extensions.Logging
软件包(和源代码)。
尝试使用类似以下内容注入上下文:
kernel.Bind<IDependency>()
.To<Mydependency>()
.InRequestScope()
.WithConstructorArgument("context",c=>HttpContext.Current);
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来通过路由模板过滤路由。
编辑:在您的意见后,我意识到路由配置不是你以后的。
如果最终目标是在控制器中实现日志记录,则可能需要查看ASP.NET Web API中的跟踪,因为支持Web API基础架构内置的跟踪。
链接地址: http://www.djcxy.com/p/11293.html上一篇: Request Parameters Outside Controller
下一篇: What is the effect (or purpose) of a foreign key column referencing itself?