ASP.NET Core 7中间件如何读取请求与响应体并输出到控制台?
ASP.NET Core 7 最小API中间件读取响应体问题解决
核心问题解析
1. 中间件执行流程误解
你提到的“中间件被调用两次”是对管道模型的误解:中间件只会被调用一次,但InvokeAsync方法内的代码分为两个阶段:
await _next(httpContext)之前:请求流入阶段,此时端点还未处理请求,响应体尚未生成await _next(httpContext)之后:响应流出阶段,此时端点已处理完请求,响应体已写入流中,这才是读取响应体的正确时机
2. 响应体流不可读的原因
ASP.NET Core默认的Response.Body是只写流(HttpResponseStream),不支持读取操作,直接尝试读取就会抛出Stream was not readable异常。必须先将其替换为可读写的MemoryStream,才能捕获并读取响应内容。
完整解决方案代码
public class RequestResponseLoggingMiddleware { private readonly RequestDelegate _next; public RequestResponseLoggingMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext httpContext) { // 读取请求体(保留你原有可用的代码) try { httpContext.Request.EnableBuffering(); string requestBody = await new StreamReader(httpContext.Request.Body, Encoding.UTF8).ReadToEndAsync(); httpContext.Request.Body.Position = 0; Console.WriteLine($"Request body: {requestBody}"); } catch (Exception ex) { Console.WriteLine($"Exception reading request: {ex.Message}"); } // 准备捕获响应体 var originalResponseBody = httpContext.Response.Body; using var responseBodyStream = new MemoryStream(); httpContext.Response.Body = responseBodyStream; try { // 传递请求到后续中间件/端点,此时端点会将响应写入responseBodyStream await _next(httpContext); // 读取响应体 responseBodyStream.Seek(0, SeekOrigin.Begin); string responseBody = await new StreamReader(responseBodyStream, Encoding.UTF8).ReadToEndAsync(); responseBodyStream.Seek(0, SeekOrigin.Begin); Console.WriteLine($"Response body: {responseBody}"); // 将响应内容复制回原始响应流,确保客户端能收到响应 await responseBodyStream.CopyToAsync(originalResponseBody); } catch (Exception ex) { Console.WriteLine($"Exception reading response: {ex.Message}"); } finally { // 恢复原始响应流,避免请求管道异常 httpContext.Response.Body = originalResponseBody; } } } // 中间件扩展方法,用于注册 public static class RequestResponseLoggingMiddlewareExtensions { public static IApplicationBuilder UseRequestResponseLogging(this IApplicationBuilder builder) { return builder.UseMiddleware<RequestResponseLoggingMiddleware>(); } }
关键步骤说明
- 替换响应流:在调用
_next前,将Response.Body替换为MemoryStream,让端点处理后的响应写入这个可读写流 - 读取响应体:在
await _next之后,重置MemoryStream的位置到起始处,再读取已生成的响应内容 - 恢复响应流:读取完成后,必须将
MemoryStream的内容复制回原始响应流,并恢复Response.Body为原始流,否则客户端无法接收到响应 - 异常防护:使用
finally块确保原始响应流被恢复,避免请求管道因流未重置而出现异常
注册中间件
在Program.cs中注册中间件,注意要放在端点注册(如app.MapGet/app.MapPost)之前:
var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); // 注册日志中间件 app.UseRequestResponseLogging(); // 配置你的最小API端点 app.MapPost("/api/test", (TestModel model) => Results.Ok(model)); app.Run();
内容的提问来源于stack exchange,提问作者Avrohom Yisroel
相关产品推荐
相关产品推荐

