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

时间:2012-10-18 21:03:20

标签: c# asp.net-mvc ninject asp.net-web-api httpcontext

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

修改:将问题更新为更具体,并包含更多详细信息。

3 个答案:

答案 0 :(得分:2)

尝试使用以下内容注入上下文:

kernel.Bind<IDependency>()
    .To<Mydependency>()
    .InRequestScope()
    .WithConstructorArgument("context",c=>HttpContext.Current);

答案 1 :(得分:1)

实际问题是非常中立的,你应该用Ninject做什么 - 你只需要对你的处理进行分阶段,这样任何正在运行异步的对象都拥有他们需要的一切,而不依赖于魔法{{1} }。首先使用没有DI容器的工作。

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

  1. 您的HttpContext.Current语句需要运行一次。请参阅Ninject.MV3 wiki了解最佳方法(直到它合并为止,没有OOTB与基于NuGet的版本)

  2. 正如@rickythefox(+ 1'd)所说,您的注册应该将线程/上下文相关数据烘焙到对象中并配置注册,以便在请求处理的早期发生,当您仍然在Bind

    的主题上
    HttpContext.Current
  3. 然后让处理程序的构造函数采用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基础结构的跟踪。