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

在ActionFilterAttribute的OnResultExecuted中覆盖响应体后响应为空

问题:使用ActionFilterAttribute修改HttpContext.Response.Body后无响应返回

尝试通过ActionFilterAttribute拦截并修改响应体内容(将特定词汇替换为其他内容),能成功获取并修改响应体,但将新流赋值给HttpContext.Response.Body后,Postman始终收到空响应。

原代码实现

过滤器类

public class MyActionFilter : ActionFilterAttribute
{
    private readonly ILogger<MyMessageFilter> _logger;
    private MemoryStream _responseBody;

    public MyActionFilter(ILogger<MyMessageFilter> logger)
    {
        _logger = logger;
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
       _logger.LogInformation("OnActionExecuting");
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        this._responseBody = new MemoryStream();
        context.HttpContext.Response.Body = _responseBody;
        _logger.LogInformation("OnActionExecuted");
    }

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        _logger.LogInformation("OnResultExecuting");
    }

    public override void OnResultExecuted(ResultExecutedContext context)
    {
         _responseBody.Seek(0, SeekOrigin.Begin);

        //read the memory stream 
        using var sr = new StreamReader(_responseBody);
        
        // Old stream
        var oldResponseBody = sr.ReadToEnd();

        var memStream = new MemoryStream();
        context.HttpContext.Response.Body = memStream;
        memStream.Position = 0;
        
        // Replace old with
        var responseBody = oldResponseBody.Replace("Anything", "Something", StringComparison.InvariantCultureIgnoreCase);
        var stream = GenerateStreamFromString(responseBody);

        context.HttpContext.Response.Body = stream;
        base.OnResultExecuted(context);
    }

    public static Stream GenerateStreamFromString(string s)
    {
        var stream = new MemoryStream();
        var writer = new StreamWriter(stream);
        writer.Write(s);
        writer.Flush();
        stream.Position = 0;
        return stream;
    }
}

控制器应用

[HttpPost]
[TypeFilter(typeof(MyMessageFilter))]
[ActionName("WeatherForecastWeek")]
public WeatherForecastResponse GetWeekForecast()
{
  ...
}

问题分析

  1. 流替换时机错误:在OnActionExecuted中替换响应流过早,后续结果执行流程的内容写入逻辑未适配,且未保存原始响应流,导致修改后的内容无法输出到客户端。
  2. 多次无意义替换Body:OnResultExecuted是结果执行完成后的回调,此时框架已完成响应输出处理,再修改Response.Body不会生效,多次替换流属于无效操作。
  3. 未将修改内容写入原始流:修改后的内容仅存于新创建的流中,未拷贝回客户端实际接收的原始响应流,导致客户端收不到内容。

修复后的代码

public class MyActionFilter : ActionFilterAttribute
{
    private readonly ILogger<MyActionFilter> _logger;
    private Stream _originalResponseBody;
    private MemoryStream _responseCaptureStream;

    public MyActionFilter(ILogger<MyActionFilter> logger)
    {
        _logger = logger;
    }

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        _logger.LogInformation("OnResultExecuting");
        // 保存客户端实际接收的原始响应流
        _originalResponseBody = context.HttpContext.Response.Body;
        // 创建内存流用于捕获响应内容
        _responseCaptureStream = new MemoryStream();
        context.HttpContext.Response.Body = _responseCaptureStream;
    }

    public override async Task OnResultExecutedAsync(ResultExecutedContext context, CancellationToken cancellationToken)
    {
        _logger.LogInformation("OnResultExecuted");
        try
        {
            // 重置内存流位置,读取捕获到的响应内容
            _responseCaptureStream.Seek(0, SeekOrigin.Begin);
            using var reader = new StreamReader(_responseCaptureStream);
            var responseContent = await reader.ReadToEndAsync(cancellationToken);
            
            // 执行内容替换逻辑
            var modifiedContent = responseContent.Replace("Anything", "Something", StringComparison.InvariantCultureIgnoreCase);
            
            // 将修改后的内容写入内存流,再拷贝到原始响应流
            _responseCaptureStream.SetLength(0);
            using var writer = new StreamWriter(_responseCaptureStream, leaveOpen: true);
            await writer.WriteAsync(modifiedContent);
            await writer.FlushAsync();
            
            _responseCaptureStream.Seek(0, SeekOrigin.Begin);
            await _responseCaptureStream.CopyToAsync(_originalResponseBody, cancellationToken);
        }
        finally
        {
            // 恢复原始响应流,释放临时内存流
            context.HttpContext.Response.Body = _originalResponseBody;
            _responseCaptureStream?.Dispose();
        }
        
        await base.OnResultExecutedAsync(context, cancellationToken);
    }
}

修复说明

  • 调整流替换时机:在OnResultExecuting中替换响应流,确保框架执行结果时能将内容写入临时内存流。
  • 保存并恢复原始流:保留客户端实际接收的响应流,修改完成后将内容拷贝回去,保证内容能正确输出。
  • 异步流操作:使用异步API避免阻塞,同时保证流读写操作的完整性。
  • 规范流操作顺序:重置流位置、写入修改内容、拷贝到原始流,确保所有操作符合流的读写规则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 11:49:53