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

.NET 6请求取消后中间件无法拦截响应体的原因

请求取消后中间件无法获取响应体的原因解析

问题描述

在.NET 6 Web API中编写了用于处理响应体的中间件,发现若在处理完成前请求被取消,中间件会获取到空的响应体。将处理逻辑迁移到Action过滤器后,即便请求被取消,仍能成功获取响应体。

中间件代码

public class CustomResponseMiddleware
{
    private readonly RequestDelegate _next;
    public CustomResponseMiddleware(RequestDelegate next)
    {
        _next = next;
    }
    public async Task Invoke(HttpContext httpContext)
    {
        var originalBodyStream = httpContext.Response.Body;
        await using var memoryStream = new MemoryStream();
        httpContext.Response.Body = memoryStream;

        await _next(httpContext);

        memoryStream.Seek(0, SeekOrigin.Begin);
        // The responseBody will be as empty string ("")
        var responseBody = await new StreamReader(memoryStream).ReadToEndAsync(); 
        memoryStream.Seek(0, SeekOrigin.Begin);

        //Do some processing on the response body here

        httpContext.Response.Body = originalBodyStream;
        await httpContext.Response.Body.WriteAsync(memoryStream.ToArray());
    }
}

原因解析

核心差异在于中间件与Action过滤器在请求处理管道中的执行时机和所处层级不同,以及ASP.NET Core对请求取消的处理逻辑差异:

  • 中间件的执行层级与请求取消的影响
    中间件处于整个请求处理管道的链式执行流程中,await _next(httpContext)会将请求传递到后续中间件直至MVC框架。当请求被取消(如客户端断开连接),ASP.NET Core触发HttpContext.RequestAborted取消令牌,此时MVC框架会直接终止响应写入流程——既然客户端已经不需要响应,就不会把生成好的响应内容写入你替换的memoryStream,导致后续读取到空内容。

  • Action过滤器的执行时机
    Action过滤器属于MVC框架内部的生命周期环节,它在动作方法执行完成、响应内容已生成后运行。此时不管后续请求是否被取消,过滤器已经能拿到动作方法生成的完整响应体(MVC会先完成响应内容生成,再处理请求取消的后续逻辑),因此即便请求被取消,依然可以正常获取响应体。

  • 中间件代码的潜在问题
    你的中间件未处理请求取消引发的OperationCanceledException,当_next因请求取消抛出异常时,后续读取流的代码可能无法执行,或者流本身就没有被写入任何内容。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 02:51:19