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

ASP.NET Core 7中间件如何读取请求与响应体并输出到控制台?

ASP.NET Core 7 最小API中间件读取响应体问题解决

核心问题解析

1. 中间件执行流程误解

你提到的“中间件被调用两次”是对管道模型的误解:中间件只会被调用一次,但InvokeAsync方法内的代码分为两个阶段:

  • await _next(httpContext)之前:请求流入阶段,此时端点还未处理请求,响应体尚未生成
  • await _next(httpContext)之后:响应流出阶段,此时端点已处理完请求,响应体已写入流中,这才是读取响应体的正确时机

2. 响应体流不可读的原因

ASP.NET Core默认的Response.Body是只写流(HttpResponseStream),不支持读取操作,直接尝试读取就会抛出Stream was not readable异常。必须先将其替换为可读写的MemoryStream,才能捕获并读取响应内容。

完整解决方案代码

public class RequestResponseLoggingMiddleware
{
    private readonly RequestDelegate _next;

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

    public async Task InvokeAsync(HttpContext httpContext)
    {
        // 读取请求体(保留你原有可用的代码)
        try
        {
            httpContext.Request.EnableBuffering();
            string requestBody = await new StreamReader(httpContext.Request.Body, Encoding.UTF8).ReadToEndAsync();
            httpContext.Request.Body.Position = 0;
            Console.WriteLine($"Request body: {requestBody}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Exception reading request: {ex.Message}");
        }

        // 准备捕获响应体
        var originalResponseBody = httpContext.Response.Body;
        using var responseBodyStream = new MemoryStream();
        httpContext.Response.Body = responseBodyStream;

        try
        {
            // 传递请求到后续中间件/端点,此时端点会将响应写入responseBodyStream
            await _next(httpContext);

            // 读取响应体
            responseBodyStream.Seek(0, SeekOrigin.Begin);
            string responseBody = await new StreamReader(responseBodyStream, Encoding.UTF8).ReadToEndAsync();
            responseBodyStream.Seek(0, SeekOrigin.Begin);

            Console.WriteLine($"Response body: {responseBody}");

            // 将响应内容复制回原始响应流,确保客户端能收到响应
            await responseBodyStream.CopyToAsync(originalResponseBody);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Exception reading response: {ex.Message}");
        }
        finally
        {
            // 恢复原始响应流,避免请求管道异常
            httpContext.Response.Body = originalResponseBody;
        }
    }
}

// 中间件扩展方法,用于注册
public static class RequestResponseLoggingMiddlewareExtensions
{
    public static IApplicationBuilder UseRequestResponseLogging(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<RequestResponseLoggingMiddleware>();
    }
}

关键步骤说明

  1. 替换响应流:在调用_next前,将Response.Body替换为MemoryStream,让端点处理后的响应写入这个可读写流
  2. 读取响应体:在await _next之后,重置MemoryStream的位置到起始处,再读取已生成的响应内容
  3. 恢复响应流:读取完成后,必须将MemoryStream的内容复制回原始响应流,并恢复Response.Body为原始流,否则客户端无法接收到响应
  4. 异常防护:使用finally块确保原始响应流被恢复,避免请求管道因流未重置而出现异常

注册中间件

在Program.cs中注册中间件,注意要放在端点注册(如app.MapGet/app.MapPost)之前:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// 注册日志中间件
app.UseRequestResponseLogging();

// 配置你的最小API端点
app.MapPost("/api/test", (TestModel model) => Results.Ok(model));

app.Run();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 16:42:34