ASP.NET Core中间件中读取Request.Body和Response.Body需启用缓冲并重置流位置,日志应采样记录关键字段且避免敏感信息。

中间件里怎么读取 Request.Body 和 Response.Body
ASP.NET Core 的 Request.Body 和 Response.Body 默认是只读/只写流,且只能读一次。直接在中间件里 ReadAsync 会导致后续控制器拿不到原始 body —— 这是最常踩的坑。
必须先启用缓冲(buffering),再用 StreamReader 或 JsonDocument.Parse 解析内容。但注意:启用缓冲会增加内存开销,尤其对大文件上传场景要谨慎。
- 对
Request.Body:调用HttpContext.Request.EnableBuffering()(.NET 5+)或手动复制到MemoryStream - 对
Response.Body:需用Response.Body = new MemoryStream()替换,并在写入后回拷到原始流,否则客户端收不到响应 - 务必在
await next();前重置Request.Body.Position = 0,否则 Controller 读不到数据
日志内容里哪些字段真正有用
不是所有请求都值得全量记录。比如 /health、/metrics 或静态资源路径(.js、.css)打满日志只会掩盖问题。重点该记的是:真实业务 API 的完整输入输出 + 关键上下文。
- 必记:HTTP 方法、路径、状态码、耗时(
Stopwatch计时)、客户端 IP(HttpContext.Connection.RemoteIpAddress) - 建议记:请求头中
Authorization(脱敏后)、CorrelationId、User-Agent - 避免记:二进制 body(如图片上传)、超长 JSON(可截断前 2KB)、敏感字段(密码、token 字符串,用
Regex.Replace清洗)
为什么不能直接在 Controller 里记日志
Controller 层日志天然缺失关键信息:比如中间件抛出的异常(400 Bad Request 由模型绑定失败触发)、CORS 拒绝、认证失败(401)、甚至请求根本没进 Controller(路由 404)。这些在中间件外就结束了。
- Controller 日志只覆盖“成功抵达并执行完”的请求,覆盖率低于 60%
- 中间件能统一捕获
try/catch外的异常,包括JsonException、InvalidOperationException - 如果用
ILogger<t></t>在中间件里打日志,确保注入的是ILoggerFactory创建的实例,而非单例 logger,否则作用域可能错乱
.NET 6+ 中如何避免性能暴跌
全量序列化 body + 同步写磁盘会让吞吐量掉 3–5 倍。核心矛盾是:日志要完整,但不能拖慢主流程。
- 用
System.Text.Json而非Newtonsoft.Json,前者无反射、零分配解析更轻量 - 日志写入必须异步:用
ILogger.LogInformation(底层已异步),别自己开线程或用Task.Run - 加采样开关:开发环境全量,生产环境按百分比(如 5%)或仅记录错误响应(
context.Response.StatusCode >= 400) - 避免在日志里拼接字符串(
"req: " + body),改用结构化日志占位符:logger.LogInformation("Req {Method} {Path} {StatusCode}", method, path, code)
最易被忽略的是 body 流位置重置时机和采样策略——漏掉前者,API 直接 400;漏掉后者,上线第一天磁盘就写爆。


















