在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() { ... }
问题分析
- 流替换时机错误:在
OnActionExecuted中替换响应流过早,后续结果执行流程的内容写入逻辑未适配,且未保存原始响应流,导致修改后的内容无法输出到客户端。 - 多次无意义替换Body:
OnResultExecuted是结果执行完成后的回调,此时框架已完成响应输出处理,再修改Response.Body不会生效,多次替换流属于无效操作。 - 未将修改内容写入原始流:修改后的内容仅存于新创建的流中,未拷贝回客户端实际接收的原始响应流,导致客户端收不到内容。
修复后的代码
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
相关产品推荐
相关产品推荐

