我正在为 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/