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

如何在端点执行完成后通过中间件修改ASP.NET Core响应体?

解决ASP.NET Core中间件读取响应体时的Stream不支持读取异常

问题原因

ASP.NET Core默认的Response.Body是只写流(Write-only),不支持读取操作,直接调用CopyToAsync会触发System.NotSupportedException。你之前尝试在OnStarting回调中读取响应体,不仅因为流本身不可读,还因为OnStarting触发时,端点尚未写入响应内容,此时读取也无法获取有效数据。

解决方案

核心思路是在执行后续中间件/端点前,将原始响应流替换为可读写的内存流,捕获端点输出的响应内容,修改后再写回原始流。具体实现如下:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

var app = builder.Build();

app.UseMiddleware<EditResponseMiddleware>();

app.UseAuthorization();

app.MapControllers();

app.Run();

public class EditResponseMiddleware
{
    private readonly RequestDelegate _next;

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

    public async Task InvokeAsync(HttpContext context)
    {
        // 处理请求体解密(保留原有逻辑)
        // ...

        // 保存原始响应流
        var originalResponseBody = context.Response.Body;

        // 创建可读写内存流,用于捕获响应内容
        using var responseStream = new MemoryStream();
        context.Response.Body = responseStream;

        try
        {
            // 执行后续中间件与端点逻辑,响应内容会写入内存流
            await _next(context);

            // 重置内存流位置到起始处,准备读取内容
            responseStream.Seek(0, SeekOrigin.Begin);

            // 读取响应内容(根据需求选择字符串或字节数组)
            var responseContent = await new StreamReader(responseStream).ReadToEndAsync();
            // 若需字节数组:byte[] responseBytes = responseStream.ToArray();

            // 执行响应内容修改(示例为加密逻辑,替换为你的实际代码)
            var modifiedContent = EncryptResponse(responseContent);
            var modifiedBytes = Encoding.UTF8.GetBytes(modifiedContent);

            // 清空内存流,写入修改后的内容
            responseStream.SetLength(0);
            await responseStream.WriteAsync(modifiedBytes);

            // 重置内存流位置,准备写回原始流
            responseStream.Seek(0, SeekOrigin.Begin);

            // 更新响应头Content-Length,避免客户端解析异常
            context.Response.ContentLength = modifiedBytes.Length;

            // 将修改后的内容写入原始响应流
            await responseStream.CopyToAsync(originalResponseBody);
        }
        finally
        {
            // 恢复原始响应流,避免影响后续中间件
            context.Response.Body = originalResponseBody;
        }
    }

    // 示例加密方法,根据实际需求实现
    private string EncryptResponse(string content)
    {
        // 替换为你的实际加密逻辑
        return $"Encrypted[{content}]";
    }
}

关键注意点

  • 流替换时机:必须在await _next(context)前替换响应流,确保端点输出写入内存流。
  • 流位置重置:每次读取/写入内存流后,需用Seek(0, SeekOrigin.Begin)重置位置,否则会读取空内容或写入到流末尾。
  • 更新Content-Length:修改响应内容后必须更新该响应头,否则客户端会因内容长度不匹配出现异常。
  • 恢复原始流:在finally块中恢复原始响应流,避免后续中间件出现流操作异常。

内容的提问来源于stack exchange,提问作者Q-bertsuit

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 16:57:36