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

ASP.NET Core 6部署IIS时HttpContext.Response.Body为空问题求助

问题描述

本地IIS部署的ASP.NET Core 6 REST API,约20%请求触发HttpContext.Response.Body为空的异常。Audit.NET日志显示这类请求的ResponseBody值为空,但状态码为200。未使用AWS,但症状类似相关AWS问题。怀疑Audit.NET导致,但分析中间件代码未发现问题,本地压测无法复现。

补充代码

Program.cs

var app = builder.Build();
app.Use((context, next) =>
        {
           context.Request.EnableBuffering();
           return next();
        });
app.UseExceptionHandler(GlobalExceptionHandler.Configure);
app.UseAuditMiddleware(_ => _
                            .FilterByRequest(r => r.Path.Value?.Contains("/api/") == true)
                            .WithEventType(context => context.Request.Path)
                            .IncludeRequestBody()
                            .IncludeResponseBody());
  
app.UseMiddleware<ExceptionHandlingMiddleware>();
app.UseSwagger();
app.MapControllers();

GlobalExceptionHandler.cs

internal class GlobalExceptionHandler
{
   public static void Configure(IApplicationBuilder applicationBuilder)
   {
      applicationBuilder.Run(async context =>
      {
         context.Response.StatusCode = StatusCodes.Status500InternalServerError;
         context.Response.ContentType = "application/json";
     
         var status = context.Features.Get<IStatusCodeReExecuteFeature>();
         var error = context.Features.Get<IExceptionHandlerFeature>();
         if (error != null)
         {
            var responseDetails = GetFullExceptionDetails(status, error.Error);
            await context.Response.WriteAsync(responseDetails, Encoding.UTF8);
         }
      });
   }
}

ExceptionHandlingMiddleware.cs

internal class ExceptionHandlingMiddleware
{
   private readonly RequestDelegate _next;

   public ExceptionHandlingMiddleware(RequestDelegate next)
   {
      _next = next;
   }

   public async Task InvokeAsync(HttpContext httpContext)
   {
      try
      {
         await _next(httpContext);
      }
      catch (Exception exception)
      {
         var errorResponse = HandleBusinessException(exception as MyException);
         var statusCode = GetStatusCode(exception as MyException);
 
         httpContext.Response.StatusCode = statusCode;
         httpContext.Response.ContentType = "application/json";
         await httpContext.Response.WriteAsync(errorResponse.Serialize());
      }
   }

   private object HandleBusinessException(MyException ex) => new { Message = ex?.Message };
   private int GetStatusCode(MyException ex) => ex != null ? 400 : 500;
}

问题分析与解决方案

1. 中间件顺序错误

当前UseAuditMiddleware注册在ExceptionHandlingMiddleware之前,会导致Audit.NET先捕获响应流,但后续异常处理中间件执行时,可能因响应流未正确重置或被Audit.NET提前释放,引发Response.Body为空的问题。

修复方案:调整中间件顺序,让异常处理逻辑先执行,再进入Audit记录流程:

var app = builder.Build();
app.Use((context, next) =>
        {
           context.Request.EnableBuffering();
           return next();
        });
app.UseExceptionHandler(GlobalExceptionHandler.Configure);
// 先注册异常处理中间件
app.UseMiddleware<ExceptionHandlingMiddleware>();
// 再注册Audit中间件
app.UseAuditMiddleware(_ => _
                            .FilterByRequest(r => r.Path.Value?.Contains("/api/") == true)
                            .WithEventType(context => context.Request.Path)
                            .IncludeRequestBody()
                            .IncludeResponseBody());
  
app.UseSwagger();
app.MapControllers();

2. Audit.NET响应流捕获的并发问题

高并发场景下,Audit.NET默认的响应流读取逻辑可能出现流未初始化或提前释放的情况。可以通过启用内置缓冲或手动包装响应流解决:

方案A:启用Audit.NET内置响应缓冲

app.UseAuditMiddleware(_ => _
                            .FilterByRequest(r => r.Path.Value?.Contains("/api/") == true)
                            .WithEventType(context => context.Request.Path)
                            .IncludeRequestBody()
                            .IncludeResponseBody()
                            .UseResponseBuffering()); // 启用响应缓冲

方案B:手动包装响应流

在请求管道最前端添加流缓冲逻辑,确保响应流始终可用:

app.Use(async (context, next) =>
{
    var originalBody = context.Response.Body;
    using var memoryStream = new MemoryStream();
    context.Response.Body = memoryStream;

    try
    {
        await next();
        memoryStream.Position = 0;
        await memoryStream.CopyToAsync(originalBody);
    }
    finally
    {
        context.Response.Body = originalBody;
    }
});

3. IIS环境配置优化

IIS在高并发场景下可能出现响应流初始化异常,检查以下配置:

  • 将应用池的.NET CLR版本设置为No Managed Code(ASP.NET Core为自托管模式,无需IIS托管CLR)
  • 调整应用池队列长度和最大工作进程数,避免请求排队引发资源竞争
  • 启用IIS的Failed Request Tracing,捕获异常请求的完整处理流程,定位流为空的具体触发时机

4. 异常处理中间件代码修复

原代码存在变量名错误(未定义context,应使用httpContext)和构造函数缺失的问题,这些错误可能导致中间件执行异常,间接引发响应流问题,修正后的代码已在补充代码部分给出。

5. 本地压测复现建议

生产环境的并发量、请求模式(如长连接、突发流量)与本地差异较大,建议使用工具模拟接近生产的流量模式(如混合长短请求、匹配生产并发数),同时在生产环境添加日志,记录Response.Body为空时的请求路径、请求头、上下文信息,辅助定位问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 05:30:13