请求参数外部控制器

我正在开发一个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是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?