c# - 如何在 ASP.NET Core 中间件中获取传入请求正文和传出响应正文?

标签 c# asp.net-core minimal-apis

我正在为 ASP.NET Core 7 最小 API 项目编写一些中间件。

在这个阶段,我只想能够读取正在发送的请求正文,然后读取正在发送的响应正文,并将它们转储到控制台。

阅读请求似乎很容易。我的中间件包含以下内容...

  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}");
    }

    await _next(httpContext);
  }

这很好用。但是,当我尝试读取响应正文时,出现异常。我尝试插入以下内容(类似于在许多 SO 答案和博客文章中找到的内容)...

    try {
      using StreamReader reader = new(httpContext.Response.Body);
      httpContext.Response.Body.Seek(0, SeekOrigin.Begin);
      string responseBody = await reader.ReadToEndAsync();
      httpContext.Response.Body.Seek(0, SeekOrigin.Begin);
      Console.WriteLine($"Response body: {responseBody}");
    } catch (Exception ex) {
      Console.WriteLine($"Exception reading response: {ex.Message}");
    }

但是,这会产生异常System.ArgumentException:流不可读。

令我困惑的一件事是,根据the Microsoft docs ,中间件被调用两次,一次是在请求传入时(在调用特定端点代码之前),第二次是在发送响应时(在端点代码完成其工作之后)。我可以看到我的中间件仅在请求传入时被调用,因此即使上面的代码有效,我也不知道它将如何获得响应,因为端点代码尚未生成响应。

我四处搜寻,但没有找到任何地方可以澄清这一点。

有人能告诉我我做错了什么吗?谢谢

最佳答案

middleware is called twice, once when the request comes in (before the specific endpoint code has been called), and a second time when the response is being sent out

虽然这是可以根据文档中的文章和图片形成的印象,但它并不完全正确,中间件仅被调用一次(管道中的每次注册)。处理管道的更正确表示是俄罗斯嵌套娃娃 - 第一个注册的中间件调用第二个,依此类推。 await _next(httpContext); 是对管道中以下中间件的调用,因此如果需要你可以在它之前和之后做一些事情。

您可以尝试这样的操作 - 用您自己控制的响应流替换响应流,然后写回所有内容(我仅使用 Use 来注册中间件,但您可以将其适应具有单独类的方法):

app.Use(async (httpContext, next) =>
{
    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}");
    }

    Stream originalBody = httpContext.Response.Body;
    try
    {
        using var memStream = new MemoryStream();
        httpContext.Response.Body = memStream;

        // call to the following middleware 
        // response should be produced by one of the following middlewares
        await next(httpContext); 

        memStream.Position = 0;
        string responseBody = new StreamReader(memStream).ReadToEnd();

        memStream.Position = 0;
        await memStream.CopyToAsync(originalBody);
        Console.WriteLine(responseBody);
    }
    finally
    {
        httpContext.Response.Body = originalBody;
    }
});

我还强烈建议您查看HttpLoggingMiddleware从框架提供的HTTP logging support它的作用完全相同(也许以更正确/性能更好的方式)。

关于c# - 如何在 ASP.NET Core 中间件中获取传入请求正文和传出响应正文?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76316856/

相关文章:

c# - 非托管代码中的 ASP.NET Core 1.1 和 EF 6 异常

c# - MapGet管道中的执行顺序

c# - 连接oracle的odbc指令

c# - 如何将 XML block 从一个文档复制到另一个文档?

c# - 如何计算 LINQ to SQL 中表中的 true 数

c# - 如何摆脱 .net core 2.2 中的 CORS?

c# - 使用 Moq 测试是否调用了外部库的方法

c# - options.UseOracle() 在 EF Core 中不可用

c# - 为什么 'foo' 返回空主体?