You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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;
        }
    }
}

注意事项

  1. 中间件注册顺序:确保响应日志中间件在请求日志中间件之后注册,同时要在UseRouting、UseEndpoints等业务相关中间件之前,保证响应流替换逻辑能覆盖后续的业务处理:
    // Startup.cs 或 Program.cs(.NET 6+)
    app.UseMiddleware<RequestLoggingMiddleware>();
    app.UseMiddleware<ResponseLoggingMiddleware>();
    
    app.UseRouting();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
    
  2. 编码处理:如果你的API使用非UTF-8编码(比如GBK),需要在StreamReader的构造函数中指定对应的编码,避免乱码。
  3. 大响应内容:如果你的API会返回非常大的响应(比如超过几百MB),使用MemoryStream可能会导致内存占用过高,这种情况可以考虑使用临时文件流替代。

这样修改后,你的响应日志就能正常读取响应体,同时客户端也能收到完整的响应内容了。

内容的提问来源于stack exchange,提问作者user13755987

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 17:02:56