Web Api - 控制器外部的请求参数

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中的日志调用,但我真的想要一个更通用的解决方案,我总是可以依赖日志记录中的版本信息.

编辑:将问题更新为更具体,并包含更多详细信息.

Rub*_*ink 1

实际问题对于您应该如何使用 Ninject 来说实际上是中立的 - 您只需要确定处理的阶段性,以便任何将要异步运行的对象都拥有它们所需的一切,而无需依赖魔法HttpContext.Current。首先在没有 DI 容器的情况下使用它。

然后,使用 Ninject 的主要步骤是:-

  1. 您的Bind语句需要运行一次。请参阅 Ninject.MV3 wiki 了解最佳方法(在合并之前,基于 NuGet 的版本不存在 OOTB)

  2. 正如 @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包(和源代码)。