9

Web APIリクエストコンテンツ(つまり、json文字列)をログアウトしようとしています。ITraceWriterクラス()を実装し、WebAPIがパイプラインで呼び出すように構成しました。しかし、request.Contentを読み取ったり、ストリームにコピーして読み取ったりすると、メソッドで使用できなくなり、モデルがnullになります。 この投稿では、その問題について少し説明しています。インバウンドWebAPIリクエストコンテンツをログアウトした経験があり、最善のアプローチを知っている人はいますか?

ありがとう

アップデートA

プロジェクト内のすべてを除外するために、単純なサンプルWeb APIプロジェクトを作成しましたが、ロギングのためにモデルがnullになることがわかります。Fidderを介して投稿することにより、連続して数回テストするだけで、モデルがnullになることを確認します。ブレークポイントを設定すると、機能する可能性があります。そのため、同期/タイミングの問題があると思います。これを機能させる方法について何か考えはありますか?

ヘッダ:

User-Agent: Fiddler
Host: localhost:56824
Content-Type: application/json
Content-Length: 22

体:

{
"A":1,"B":"test"
}

コードは次のとおりです。

コントローラ:

public class ValuesController : ApiController
{
    [HttpPost]
    public void Post(ValuesModel model)
    {
        if (model == null)
        {
            Debug.WriteLine("model was null!");
        }
        else
        {
            Debug.WriteLine("model was NOT null!");
        }
    }
}

モデル:

public class ValuesModel
{
    public int A { get; set; }
    public string B { get; set; }
}

ロガー:

public class APITraceLogger : DelegatingHandler
    {
        protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            if (request.Content != null)
            {
                // This can cause model to be null
                request.Content.ReadAsStringAsync().ContinueWith(s =>
                {
                    string requestText = s.Result;
                    Debug.WriteLine(requestText);
                });

                // and so can this
                //request.Content.ReadAsByteArrayAsync()
                //    .ContinueWith((task) =>
                //    {
                //        string requestText = System.Text.UTF8Encoding.UTF8.GetString(task.Result);
                //        Debug.WriteLine(requestText);
                //    });
            }
            // Execute the request, this does not block
            var response = base.SendAsync(request, cancellationToken);

            // TODO:
            // Once the response is processed asynchronously, log the response data
            // to the database


            return response;
        }


    }

WebApiConfigクラスでのロガーの配線:

config.MessageHandlers.Add(new APITraceLogger());

アップデートB

ロガーを次のコードに変更してawait、asyncを追加し、結果を返すと、現在は機能しているようです。非同期コードで理解していないことや、本当にタイミングの問題などのようです。

public class APITraceLogger : DelegatingHandler
{
    protected async override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        if (request.Content != null)
        {

            // This does seem to work - is it because it is synchronous?  Is this a potential problem?
            var requestText = await request.Content.ReadAsStringAsync();
            Debug.WriteLine(requestText);
        }
        // Execute the request, this does not block
        var response = base.SendAsync(request, cancellationToken);

        // TODO:
        // Once the response is processed asynchronously, log the response data
        // to the database


        return response.Result;
    }


}
4

1 に答える 1

6

その投稿でFilipが言及しているように、ReadAsStringAsyncまたはReadAsByteArrayAsyncメソッドはリクエストコンテンツを内部的にバッファリングします。これは、着信要求のストリームタイプがバッファリングされていないストリームであっても、たとえばメッセージハンドラでReadAsStringAsync / ReadAsByteArrayAsyncを安全に実行でき、モデルバインディングが正常に機能することを期待できることを意味します。

デフォルトでは、リクエストのストリームはウェブホストとセルフホストの両方の場合にバッファリングされます。ただし、ReadAsStringAsync / ReadAsByteArrayAsyncを使用していて、モデルの入札が非バッファーモードでも正常に機能するかどうかを確認する場合は、次の方法で非バッファーモードを強制できます。

public class CustomBufferPolicySelector : WebHostBufferPolicySelector
{
    public override bool UseBufferedInputStream(object hostContext)
    {
        //NOTE: by default, the request stream is always in buffered mode.
        //return base.UseBufferedInputStream(hostContext);

        return false;
    }
}

config.Services.Replace(typeof(IHostBufferPolicySelector), new CustomBufferPolicySelector());

参考までに...上記のポリシーセレクターは現在、Webホストでのみ機能します。SelfHostで同様のテストを実行する場合は、次の手順を実行します。

//NOTE: by default, the transfer mode is TransferMode.Buffered
config.TransferMode = System.ServiceModel.TransferMode.StreamedRequest;

上記の投稿の更新Bの後:

以下のようにハンドラーを変更できます。

public class LoggingHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (request.Content != null)
        {
            string requestContent = await request.Content.ReadAsStringAsync();
        }

        HttpResponseMessage response = await base.SendAsync(request, cancellationToken);

        if (response.Content != null)
        {
            string responseContent = await response.Content.ReadAsStringAsync();
        }

        return response;
    }
}
于 2013-03-19T20:31:43.867 に答える