简体   繁体   English

在ASP.Net Web API中的日志记录DelegatingHandler中读取HttpRequestMessage.Content时丢失

[英]HttpRequestMessage.Content is lost when it is read in a logging DelegatingHandler in ASP.Net Web API

When trying to an object in an Action in a Controller it sporadically seems to be null. 在Controller中的Action中尝试对象时,偶尔似乎是null。 I discovered that it is due to the ReadAsStringAsync() in the SendAsync() override of the DelegatingHandler . 我发现,这是由于ReadAsStringAsync()SendAsync()中的覆盖DelegatingHandler The issue is with the content. 问题在于内容。 When my client sends a content body and it is read in the logger it is never read by the Controller Action Invoker (or may be somewhere in the JsonFormatter ). 当我的客户端发送一个内容主体并在记录器中读取它时,它永远不会被Controller Action Invoker读取(或者可能在JsonFormatter某个地方)。 I suspect the subsequent call to Content.ReadAsStringAsync() doesnt throw an exception but also doesnt not return the expected content body (some info is returned stating that the async read is completed). 我怀疑对Content.ReadAsStringAsync()的后续调用不会抛出异常,但也不会返回预期的内容体(返回一些信息表明异步读取已完成)。

But my problem remains since I want to read a [FromBody] parameter in an action and it is null when the RaceCondition of Content.ReadStringAsync is won by the DelegatingHandler . 但我的问题仍然存在,因为我想读一个[FromBody]在动作参数时的竞争危害是空Content.ReadStringAsync是由荣获DelegatingHandler When JsonFormatter wins it though, I get the object but that is rare (only at service startup). JsonFormatter获胜时,我得到了对象,但这很少见(仅在服务启动时)。

Here is my DelegatingHandler code: 这是我的DelegatingHandler代码:

public class LogHandler : DelegatingHandler
{

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var apiRequest = new WebApiUsageRequest(request);
        WriteLog(apiRequest);
        request.Content.ReadAsStringAsync().ContinueWith(t =>
        {
            apiRequest.Content = t.Result;
            WriteLog(apiRequest);
        });

        return base.SendAsync(request, cancellationToken).ContinueWith(task =>
        {
            var apiResponse = new WebApiUsageResponse(task.Result);
            apiResponse.Content = task.Result.Content != null ? task.Result.Content.ReadAsStringAsync().Result : null;
            WriteLog(apiResponse);
            return task.Result;
        });
    }
}

Does anyone have a clue to the solution of this issue? 有没有人有解决这个问题的线索?

This is by design . 这是设计的 In ASP.NET Web API the body content is treated a forward-only stream that can only be read once. 在ASP.NET Web API中,正文内容被视为只能读取一次的仅向前流。

You might try utilising ASP.NET Web API Tracing but I haven't test it with POST request yet so I'm not sure how/if it is tracing the request body (it is tracing parameters for GET request for sure). 您可能尝试使用ASP.NET Web API跟踪,但我还没有使用POST请求进行测试,所以我不确定它是如何跟踪请求正文(它确实是跟踪GET请求的参数)。 You can read more here: 你可以在这里阅读更多:

ReadAsStreamAsync method returns the body content. ReadAsStreamAsync方法返回正文内容。

var body = string.Empty;
using (var reader = new StreamReader(request.Content.ReadAsStreamAsync().Result))
{
    reader.BaseStream.Seek(0, SeekOrigin.Begin);
    body = reader.ReadToEnd();
}

Here's what I ended up doing: 这是我最终做的事情:

public string SafelyReadContent(HttpRequestMessage request)
{
    var stream = request.Content.ReadAsStreamAsync().Result;
    var reader = new StreamReader(stream);
    var result = reader.ReadToEnd();
    stream.Seek(0, SeekOrigin.Begin);

    return result;
}

@pirimoglu's answer of using a "using" block didn't work for me, since when the reader was disposed, the underlying stream was also closed. @pirimoglu使用“使用”块的答案对我不起作用,因为当阅读器被处理时,底层流也被关闭。

but if you use the code below in the SendAsync it works properly 但如果你在SendAsync中使用下面的代码,它可以正常工作

        if (request.Content != null)
        {
            request.Content.ReadAsByteArrayAsync().ContinueWith
                (
                    (task) =>
                    {

                            var xxx = System.Text.UTF8Encoding.UTF8.GetString(task.Result);
                    });
        }
        return base.SendAsync(request, cancellationToken) //than call the base

. . .

This worked for me: 这对我有用:

using (var stream = new MemoryStream())
{
    var context = (HttpContextBase)Request.Properties["MS_HttpContext"];
    context.Request.InputStream.Seek(0, SeekOrigin.Begin);
    context.Request.InputStream.CopyTo(stream);
    string requestBody = Encoding.UTF8.GetString(stream.ToArray());
}

Returned for me the json representation of my parameter object, so I could use it for exception handling and logging. 为我返回参数对象的json表示,因此我可以将它用于异常处理和日志记录。

Found as accepted answer here 在这里找到接受的答案

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM