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

ASP.NET Core日志中间件出现MemoryStream意外关闭异常如何解决

问题根因
  • CopyToAsync执行完成后,responseStream的指针已经移动到流的末尾,即使流未关闭直接读取也无法拿到内容
  • 默认的StreamReader构造方法在实例被释放时,会自动关闭传入的底层流,你当前写法没有指定leaveOpen参数,读完内容后底层流被StreamReader主动关闭,这就是你报错的核心原因
  • 不需要移除MemoryStream的using语句,移除会导致内存无法及时释放,产生内存泄漏风险
修复方案

1. 修改中间件代码

在复制流到原始响应流之后,重新把responseStream的指针重置到起始位置,再调用日志方法:

public async Task InvokeAsync(HttpContext httpContext)
{
    Stream originalResponseStream = httpContext.Response.Body;
    try
    {
        // log the response
        using (MemoryStream responseStream = new MemoryStream())
        {
            httpContext.Response.Body = responseStream;

            await this.nextMiddleware.Invoke(httpContext);

            httpContext.Response.ContentLength = responseStream.Length;
            responseStream.Seek(0, SeekOrigin.Begin);
            await responseStream.CopyToAsync(originalResponseStream, 1024 * 16);
            // 新增:复制完成后重置流位置到开头,供日志读取
            responseStream.Seek(0, SeekOrigin.Begin);
            Logger.LogHttpResponse(httpContext, session);
        }
    }
    finally
    {
        httpContext.Response.Body = originalResponseStream;
    }
}

2. 修改日志读取方法

创建StreamReader时指定leaveOpen = true,避免StreamReader释放时关闭底层流,同时用using包裹StreamReader避免资源泄漏:

void LogHttpResponse(HttpContext context)
{
    // 指定leaveOpen为true,StreamReader释放时不会关闭底层的Response.Body流
    using var reader = new StreamReader(context.Response.Body, UTF8Encoding.UTF8, leaveOpen: true);
    var body = reader.ReadToEnd();
    // log body
}
额外注意事项
  • 大文件/二进制响应不建议直接读取整个响应体,会占用大量内存,建议添加判断,仅读取文本类型的响应内容,同时限制最大读取长度,避免内存溢出
  • 若响应是分块传输(Transfer-Encoding: chunked),不要手动设置ContentLength,避免客户端接收异常

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 20:09:02