C# HttpRequestMessage.Content在ASP.NETWebAPI中的日志删除句柄中读取时丢失

C# HttpRequestMessage.Content在ASP.NETWebAPI中的日志删除句柄中读取时丢失,c#,web-services,rest,asp.net-web-api,C#,Web Services,Rest,Asp.net Web Api,当尝试在控制器中的操作中删除对象时,它偶尔看起来是空的。我发现这是由于SendAsync()中的ReadAsStringAsync()重写了DelegatingHandler。问题在于内容。当我的客户端发送一个内容体并在记录器中读取时,控制器操作调用程序永远不会读取它(或者可能在JsonFormatter中的某个地方)。我怀疑对Content.ReadAsStringAsync()的后续调用不会引发异常,但也不会返回预期的内容正文(返回的某些信息表明异步读取已完成) 但是我的问题仍然存在,因为我

当尝试在控制器中的操作中删除对象时,它偶尔看起来是空的。我发现这是由于
SendAsync()
中的
ReadAsStringAsync()
重写了
DelegatingHandler
。问题在于内容。当我的客户端发送一个内容体并在记录器中读取时,控制器操作调用程序永远不会读取它(或者可能在
JsonFormatter
中的某个地方)。我怀疑对
Content.ReadAsStringAsync()
的后续调用不会引发异常,但也不会返回预期的内容正文(返回的某些信息表明异步读取已完成)

但是我的问题仍然存在,因为我想在操作中读取
[FromBody]
参数,当
Content.ReadStringAsync
的条件被
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;
        });
    }
}
公共类日志处理程序:DelegatingHandler
{
受保护的覆盖任务SendAsync(HttpRequestMessage请求,CancellationToken CancellationToken)
{
var apiRequest=new-WebApiUsageRequest(请求);
书面记录(APIELOG);
request.Content.ReadAsStringAsync().ContinueWith(t=>
{
aprequest.Content=t.Result;
书面记录(APIELOG);
});
返回base.sendaync(请求,取消令牌).ContinueWith(任务=>
{
var apiResponse=新的WebApiUsageResponse(task.Result);
apiResponse.Content=task.Result.Content!=null?task.Result.Content.ReadAsStringAsync()。结果:null;
写日志(apiResponse);
返回任务。结果;
});
}
}
有人知道这个问题的解决办法吗?

这是故意的。在ASP.NET Web API中,正文内容被视为只能读取一次的仅向前的流

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


但是如果在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
。 .
.ReadAsStreamAsync方法返回正文内容

var body = string.Empty;
using (var reader = new StreamReader(request.Content.ReadAsStreamAsync().Result))
{
    reader.BaseStream.Seek(0, SeekOrigin.Begin);
    body = reader.ReadToEnd();
}
这对我很有用:

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表示,因此我可以将其用于异常处理和日志记录


找到公认的答案

以下是我最后做的:

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关于使用“using”块的回答对我不起作用,因为在释放读卡器时,底层流也被关闭。

忘了提到这是RC。我怀疑RTM牙床的观察结果仍然相同。我认为对于我的要求,我可以接受不将内容写入日志。然而,对于所有试图记录内容并在POST Actions软件中使用[FromBody]的web api开发人员来说,这应该是一个“需要注意的要点”,即使用块将关闭底层steam。但在这里,我们无法接收请求头这不是同一回事。您阅读的是请求内容,而不是响应内容,这也是它工作的原因