7

ASP.NET Web Api プロジェクトに取り組んでおり、URL でバージョン情報を受け入れるようにしました。

例えば:

  • API/v1/マイコントローラー
  • API/v2/マイコントローラー

ここで、カスタム LayoutRenderer for 内でリクエスト バージョンv1、v2を取得したいと思いNlogます。通常、私は以下の例のようにこれを行います。

[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 です

これは、Async ラッパーを使用しNLogており、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 を Async スコープにバインドできますか? もしそうなら、私はまだ慣れていないので、ガイドラインをいただければ幸いです。 Ninject

当分の間、UsageHandler の Log Call にバージョン情報を直接追加しますが、ログ内のバージョン情報に常に依存できる、より一般的なソリューションが本当に必要です。

編集:質問をより具体的に更新し、詳細を含めました。

4

3 に答える 3

2

次のようなものを使用してコンテキストを注入してみてください。

kernel.Bind<IDependency>()
    .To<Mydependency>()
    .InRequestScope()
    .WithConstructorArgument("context",c=>HttpContext.Current);
于 2012-10-18T21:35:54.890 に答える
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);
    

次に、ハンドラーのコンストラクターに、ILogger割り当て可能な を取得させるだけです(これは:Dではない.Logことを願っています)static

NB、目的は、あなたがkernel.Get()、決して、ピリオドを書かないことです。

ただし、ここでの本当の問題は、WebApi の適切な使用には、HttpContext.Current他の魔法のstaticメソッドや同様のもの (テスト容易性のため、ホスティング コンテキスト (セルフ ホスティング、OWIN など) から独立させるため、およびその他の多くの理由) を使用する必要がないことです)。

また、NLog (または Log4Net) を使用している場合は、Ninject.Extensions.Loggingパッケージ (およびソース) も確認する必要があります。

于 2012-10-20T00:41:30.797 に答える
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 を使用して、ルート テンプレートを介してルートをフィルタリングできます。

編集:あなたのコメントの後、ルーティング構成があなたが望んでいたものではないことに気付きました。

最終的な目標がコントローラー内にログを実装することである場合は、Web API インフラストラクチャに組み込まれたトレースのサポートがあるため、ASP.NET Web API でのトレースを確認することをお勧めします。

于 2012-10-18T23:22:40.560 に答える