【问题标题】:HttpRequestMessage.Content is lost when it is read in a logging DelegatingHandler in ASP.Net Web API在 ASP.Net Web API 的日志记录 DelegatingHandler 中读取 HttpRequestMessage.Content 时会丢失
【发布时间】:2012-08-19 15:11:31
【问题描述】:

当尝试在控制器中的操作中的对象时,它偶尔似乎为空。我发现这是由于SendAsync() 覆盖DelegatingHandler 中的ReadAsStringAsync()。问题在于内容。当我的客户端发送内容主体并在记录器中读取它时,控制器操作调用者永远不会读取它(或者可能在JsonFormatter 中的某处)。我怀疑随后对Content.ReadAsStringAsync() 的调用不会引发异常,但也不会返回预期的内容主体(返回一些信息,说明异步读取已完成)。

但我的问题仍然存在,因为我想在操作中读取 [FromBody] 参数,并且当 Content.ReadStringAsync 的 RaceCondition 被 DelegatingHandler 赢得时,它为空。当JsonFormatter 获胜时,我得到了对象,但这种情况很少见(仅在服务启动时)。

这是我的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;
        });
    }
}

有人知道这个问题的解决方法吗?

【问题讨论】:

  • 忘了说这是 RC。我怀疑 RTM 中的观察结果也保持不变

标签: c# web-services rest asp.net-web-api


【解决方案1】:

这是设计的。在 ASP.NET Web API 中,正文内容被视为只能读取一次的只进流。

您可以尝试使用 ASP.NET Web API 跟踪,但我尚未使用 POST 请求对其进行测试,因此我不确定它如何/是否跟踪请求正文(它正在跟踪GET 请求的参数)。你可以在这里阅读更多:

【讨论】:

  • 谢谢。我认为对于我的要求,我可以忍受不编写要记录的内容。但是,对于所有尝试记录内容并在 POST 操作中使用 [FromBody] 的 Web api 开发人员来说,这应该是一个“注意点”
【解决方案2】:

ReadAsStreamAsync 方法返回正文内容。

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

【讨论】:

  • 注意 using 块会关闭底层的 steam。
【解决方案3】:

这是我最终做的:

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 使用“使用”块的答案对我不起作用,因为当阅读器被处置时,底层流也被关闭。

【讨论】:

    【解决方案4】:

    这对我有用:

    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());
    }
    

    为我返回了参数对象的 json 表示,因此我可以将其用于异常处理和日志记录。

    发现为已接受的答案here

    【讨论】:

    • 但是这里我们收不到请求头
    【解决方案5】:

    但是如果您在 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
    

    。 . .

    【讨论】:

    • 这不是一回事。您正在阅读请求内容而不是响应内容,这也是其工作的原因
    猜你喜欢
    • 1970-01-01
    • 2013-11-13
    • 1970-01-01
    • 1970-01-01
    • 2018-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多