.net core 打印请求和响应的内容

实现打印请求参数和响应结果的中间件,本以为比较容易,但是花了不少时间。

正确的代码:

    public class LogginMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger _logger;

        public LogginMiddleware(RequestDelegate next, ILogger<LogginMiddleware> logger)
        {
            _next = next;
            _logger = logger;
        }

        public async Task Invoke(HttpContext httpContext)
        {
            var req = httpContext.Request;
            req.EnableBuffering();

            using (StreamReader requestReader = new StreamReader(req.Body, Encoding.UTF8))
            {
                //log request
                var bodyStr = await requestReader.ReadToEndAsync();
                req.Body.Position = 0;

                _logger.LogInformation("Url:[{url}] ", httpContext.Request.GetDisplayUrl());
                if (!string.IsNullOrEmpty(bodyStr))
                {
                    _logger.LogInformation("Body:[{request}]", bodyStr);
                }

                using (var buffer = new MemoryStream())
                {
                    //replace the context response with our buffer
                    var stream = httpContext.Response.Body;
                    httpContext.Response.Body = buffer;

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

                    //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);
                        httpContext.Response.Body = stream;

                        _logger.LogInformation("Response:[{response}]", body);
                    }
                }
            }
        }
    }

无论对于requestresponse,都是Stream类型,当被读取后,内部的偏移会移动。而两者情况又有不同。

Request

request如果被读取后,后面的组件就无法再次读取,但是.net提供了EnableBuffering()方法允许对request重复读取。

但是这里有一点需要注意,我原本将读取的代码提取到一个单独的方法中,把request传入读取。

async Task<string> ReadBodyStr(HttpRequest req) {
      req.EnableBuffering();

      using (StreamReader requestReader = new StreamReader(req.Body, Encoding.UTF8))
      {
          var bodyStr = await requestReader.ReadToEndAsync();
          req.Body.Position = 0;
          return bodyStr;
      }
}            

这里用req.Body传入StreamReader,using结束后stream会被自动关闭,导致request也被关闭,后续的组件无法读取到任何内容。

我调试了很久,最后发现只有写在一个方法中才能让后面的组件正确获取内容

Response

Response的问题在于默认的Response不支持seek,而当后面的组件开始写入Response后,写入的内容可能已经发往客户端,我这里就读不到了。所以有了SO上的这个hack方式

即用MemoryStream替换Response中原本的Body,给后面的组件处理后,读出内容,Seek到开始位置,再写入原始的Stream中。

原文地址:https://www.cnblogs.com/mosakashaka/p/12609210.html