如何在ASP.NET Core 1.0中记录HTTP响应主体

Jan*_*ink 11 c# asp.net-core-1.0

我正在使用ASP.NET Core 1.0 RC2创建一个公共REST Api,并且喜欢记录传入请求和传出响应.

我创建了一个中间件类,在调用app.UseMvc()之前添加到管道中;

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{            
        app.UseIOMiddleware();
        app.UseMvc();            
}
Run Code Online (Sandbox Code Playgroud)

我的中间件类看起来像这样:

public class IOMiddleware
{
    private readonly RequestDelegate _next;

    public IOMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        LogRequest(context.Request);

        await _next.Invoke(context);            
    }

    private async void LogRequest(HttpRequest request)
    {
        using (var bodyReader = new StreamReader(request.Body))
        {
            string body = await bodyReader.ReadToEndAsync();

            request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body));

            System.Diagnostics.Debug.Print(body);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我可以读取请求正文流并使用此示例回放它:回滚请求正文流,但我不知道如何读取响应正文,因为流不可读.

在Web API 2.0中我可以使用HttpResponseMessage.Content.ReadAsByteArrayAsync()方法,但是如何在ASP.Net Core 1.0 RC2中完成相同的操作?

Soc*_*ock 13

问题是request.Body不可读,只能写 - 通常流会定期通过网络刷新到客户端.

您可以通过替换流并缓冲内容来完成此操作,直到管道的其余部分完成为止.

public class IOMiddleware
{
    private readonly RequestDelegate _next;

    public IOMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        await LogRequest(context.Request);

        await LogResponseAndInvokeNext(context);
    }

    private async Task LogRequest(HttpRequest request)
    {
        using (var bodyReader = new StreamReader(request.Body))
        {
            string body = await bodyReader.ReadToEndAsync();

            request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body));
            System.Diagnostics.Debug.Print(body);
        }
    }

    private async Task LogResponseAndInvokeNext(HttpContext context)
    {
        using (var buffer = new MemoryStream())
        {
            //replace the context response with our buffer
            var stream = context.Response.Body;
            context.Response.Body = buffer;

            //invoke the rest of the pipeline
            await _next.Invoke(context);

            //reset the buffer and read out the contents
            buffer.Seek(0, SeekOrigin.Begin);
            var reader = new StreamReader(buffer);
            using (var bufferReader = new StreamReader(buffer))
            {
                string body = await bufferReader.ReadToEndAsync();

                //reset to start of stream
                buffer.Seek(0, SeekOrigin.Begin);

                //copy our content to the original stream and put it back
                await buffer.CopyToAsync(stream);
                context.Response.Body = stream;

                System.Diagnostics.Debug.Print($"Response: {body}");

            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @SergueiFedorov 我看到了这种行为的一个副作用。到目前为止可以安全地传输到客户端的数据现在必须完全保存在内存中。一些本来可以成功地逐段流式传输并且对于内存中存储来说太大的东西现在将抛出 OutOfMemoryException 或类似的东西。 (2认同)

Ser*_*kov 6

不幸的是,如果用MemoryStream替换Request,则相同的流将用于将来的调用.这是错误:https: //github.com/aspnet/KestrelHttpServer/issues/940

解决方法是将Request.Body流复制到局部变量,并在最后将Body设置回原始流.

像这样:

  public async Task Invoke(HttpContext context)
    {
        //Workaround - copy original Stream
        var initalBody = context.Request.Body;

        using (var bodyReader = new StreamReader(request.Body))
        {
            string body = await bodyReader.ReadToEndAsync();
            //Do something with body
            //Replace write only request body with read/write memorystream so you can read from it later

               request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body));

        //handle other middlewares
        await _next.Invoke(context);

        //Workaround - return back to original Stream
        context.Request.Body = initalBody;
    }
Run Code Online (Sandbox Code Playgroud)

  • 问题要求回复机构,而不是要求机构. (13认同)