.NET Core Web API响应日志中间件读取响应体报错求助
修复.NET Core Web API响应日志中间件“Stream was not readable”异常
这个问题我之前也碰到过!核心原因是默认的HttpContext.Response.Body是一个只写流,不支持读取操作,所以直接用StreamReader读取肯定会抛出Stream was not readable异常。下面是完整的修复方案:
问题根源
ASP.NET Core默认的响应流是只写的(CanRead属性为false),目的是高效地向客户端发送数据。当你尝试直接读取这个流时,就会触发ArgumentException。
修复思路
我们需要把原始的响应流替换成一个可读写的内存流:
- 先保存原始响应流的引用
- 用
MemoryStream替换当前的响应流 - 让后续中间件(你的业务逻辑)把响应写入内存流
- 读取内存流中的内容完成日志记录
- 把内存流的内容复制回原始响应流,确保客户端能收到正常响应
- 重置流的位置,避免影响后续处理
完整修复代码
public class ResponseLoggingMiddleware { private readonly RequestDelegate _next; public ResponseLoggingMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext httpContext) { // 保存原始响应流的引用 var originalResponseBody = httpContext.Response.Body; try { // 创建可读写的内存流来替代原始响应流 using var responseBody = new MemoryStream(); httpContext.Response.Body = responseBody; // 执行后续中间件,让业务逻辑写入内存流 await _next(httpContext); // 重置内存流的位置到起始处,准备读取 responseBody.Seek(0, SeekOrigin.Begin); // 读取响应内容 using var reader = new StreamReader(responseBody); var bodyText = await reader.ReadToEndAsync(); Console.WriteLine($"Response Body: {bodyText}"); // 重置内存流位置,准备复制回原始流 responseBody.Seek(0, SeekOrigin.Begin); // 将内存流的内容复制回原始响应流,确保客户端能收到响应 await responseBody.CopyToAsync(originalResponseBody); } finally { // 恢复原始响应流,避免资源泄漏 httpContext.Response.Body = originalResponseBody; } } }
注意事项
- 中间件注册顺序:确保响应日志中间件在请求日志中间件之后注册,同时要在
UseRouting、UseEndpoints等业务相关中间件之前,保证响应流替换逻辑能覆盖后续的业务处理:// Startup.cs 或 Program.cs(.NET 6+) app.UseMiddleware<RequestLoggingMiddleware>(); app.UseMiddleware<ResponseLoggingMiddleware>(); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); - 编码处理:如果你的API使用非UTF-8编码(比如GBK),需要在
StreamReader的构造函数中指定对应的编码,避免乱码。 - 大响应内容:如果你的API会返回非常大的响应(比如超过几百MB),使用
MemoryStream可能会导致内存占用过高,这种情况可以考虑使用临时文件流替代。
这样修改后,你的响应日志就能正常读取响应体,同时客户端也能收到完整的响应内容了。
内容的提问来源于stack exchange,提问作者user13755987
相关产品推荐
相关产品推荐

