英文:
Logging requests for multiple endpoints without making changes in each endpoint in ASP.NET Core
问题
I am working on Asp.Net Core API, There are around 1000 endpoints which can have different bodies (request payload). I want to log the incoming requests for each endpoint. I don't want to go and make changes in each endpoint. So Is there a way to do this?
我正在开发Asp.Net Core API,有大约1000个端点,每个端点可能具有不同的请求体。我想记录每个端点的传入请求。我不想逐个端点进行更改。有没有办法做到这一点?
I have tried the below. But it logs requestBody as an empty string. Although I get the request object value in the controller action method. The below code is for HTTP POST.
我尝试过以下方法。但它将requestBody记录为空字符串。尽管我在控制器动作方法中获取了请求对象的值。以下代码是用于HTTP POST。
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// Log the request details
_logger.LogInformation($"Request: {context.HttpContext.Request.Method} {context.HttpContext.Request.Path}");
// Log the request body for POST requests
if (context.HttpContext.Request.Method.Equals("POST", StringComparison.OrdinalIgnoreCase))
{
context.HttpContext.Request.EnableBuffering();
// Read the request body
using (var reader = new StreamReader(context.HttpContext.Request.Body, encoding: Encoding.UTF8, detectEncodingFromByteOrderMarks: false, leaveOpen: true))
{
var requestBody = await reader.ReadToEndAsync();
// Reset the position of the request body stream
context.HttpContext.Request.Body.Position = 0;
_logger.LogInformation($"Request Body: {requestBody}");
}
}
// Call the action method
var resultContext = await next();
// Perform any post-action execution logic if needed
}
I have tried this, and this is working for one endpoint, but there could be many different request types, so I'll end up using multiple if-else statements, and I want to avoid it. Basically, I'm looking for a generic solution.
我尝试过这个方法,它对一个端点有效,但可能会有许多不同类型的请求,所以我最终会使用多个if-else语句,我想避免这种情况。基本上,我正在寻找一种通用解决方案。
Note: I have read some articles mentioning that it can be done using middleware, but in this question, I'm more interested in fixing the issue in my code. Or it can be fixed using ActionFilter.
注意:我已阅读一些提到可以使用中间件完成此操作的文章,但在这个问题中,我更感兴趣的是修复我的代码问题。或者可以使用ActionFilter来解决。
英文:
I am working on Asp.Net Core API, There are around 1000 endpoints which can have different bodies (request payload). I want to log the the incoming requests for each endpoint. I dont want to go and make changes in each endpoint. So Is there a way to do this?
I have tried the below. But it logs requestBody as empty string. Although I get the request object value in the controller action method. The below code is for http post.
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// Log the request details
_logger.LogInformation($"Request: {context.HttpContext.Request.Method} {context.HttpContext.Request.Path}");
// Log the request body for POST requests
if (context.HttpContext.Request.Method.Equals("POST", StringComparison.OrdinalIgnoreCase))
{
context.HttpContext.Request.EnableBuffering();
// Read the request body
using (var reader = new StreamReader(context.HttpContext.Request.Body, encoding: Encoding.UTF8, detectEncodingFromByteOrderMarks: false, leaveOpen: true))
{
var requestBody = await reader.ReadToEndAsync();
// Reset the position of the request body stream
context.HttpContext.Request.Body.Position = 0;
_logger.LogInformation($"Request Body: {requestBody}");
}
}
// Call the action method
var resultContext = await next();
// Perform any post-action execution logic if needed
}
I have tried this and this is working for one endpoint, but there could be many different request types so I’ll end up using multiple if else and I want to avoid it, basically I’m looking for a generic solution.
https://stackoverflow.com/a/67262551/6527049
Note: I have read some articles mentioning that it can be done using middleware, but in this question I’m more interested in fixing issue in my code. Or can be fixed using ActionFilter
答案1
得分: 1
首先,我强烈建议查看使用框架提供的现有HTTP日志记录基础设施,请注意,您可以使用UseWhen
有条件地启用它(就像这里或这里一样)。
如果由于某种原因它对您不适用,那么对于您的问题:
> 但它将requestBody记录为空字符串
这里有2个可能的问题(目前没有设置进行复制):
EnableBuffering
调用太晚了,请尝试将下一个中间件添加到流水线的开头(就像这里所做的那样):
app.Use((context, next) =>
{
context.Request.EnableBuffering();
return next();
});
// ...
- 流已经被读取,重新读取传入的流之前将其倒回:
// ...
context.HttpContext.Request.Body.Position = 0;
using (var reader = new StreamReader(context.HttpContext.Request.Body, encoding: Encoding.UTF8, detectEncodingFromByteOrderMarks: false, leaveOpen: true))
{
var requestBody = await reader.ReadToEndAsync();
// 重置请求体流的位置
context.HttpContext.Request.Body.Position = 0;
_logger.LogInformation($"请求体:{requestBody}");
}
还要查看这个答案。
英文:
First of all I highly recommend to look into using existing HTTP Logging infrastructure provided by the framework, note that you can enable it conditionally with UseWhen
(like it is done here or here).
If for some reason it is not sutable for you then for your problem:
> But it logs requestBody as empty string
There are 2 possible problems here (do not have setup to repro ATM):
EnableBuffering
is called too late, try adding next middleware somewhere at the start of the pipeline (like it is done here):
app.Use((context, next) =>
{
context.Request.EnableBuffering();
return next();
});
// ...
- Stream was already read, rewind the incoming stream before reading it:
// ...
context.HttpContext.Request.Body.Position = 0;
using (var reader = new StreamReader(context.HttpContext.Request.Body, encoding: Encoding.UTF8, detectEncodingFromByteOrderMarks: false, leaveOpen: true))
{
var requestBody = await reader.ReadToEndAsync();
// Reset the position of the request body stream
context.HttpContext.Request.Body.Position = 0;
_logger.LogInformation($"Request Body: {requestBody}");
}
Also check out this answer.
答案2
得分: 0
我测试了你的代码,看起来运行良好。你将请求作为 JSON 发送到 body 中吗?
请验证 Content-Type 头部。
以下是你的代码,经过了细微的修改。
class ActionFilterExample:
public class ActionFilterExample : IAsyncActionFilter
{
private readonly ILogger _logger;
public ActionFilterExample(ILogger logger)
{
_logger = logger;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// 记录请求详情
_logger.LogInformation($"请求: {context.HttpContext.Request.Method} {context.HttpContext.Request.Path}");
// 记录 POST 请求的请求体
if (context.HttpContext.Request.Method.Equals("POST", StringComparison.OrdinalIgnoreCase))
{
context.HttpContext.Request.EnableBuffering();
// 定位
context.HttpContext.Request.Body.Seek(0, SeekOrigin.Begin);
// 读取请求体
using var reader = new StreamReader(context.HttpContext.Request.Body,
encoding: Encoding.UTF8, detectEncodingFromByteOrderMarks: false, bufferSize: 8192,
leaveOpen: true);
var requestBody = await reader.ReadToEndAsync();
// 重置请求体流的位置
context.HttpContext.Request.Body.Position = 0;
_logger.LogInformation($"请求体: {requestBody}");
}
// 调用操作方法
var resultContext = await next();
// 如果需要,执行任何后操作执行逻辑
}
}
在 program.cs 中:
ILogger logger = builder.Services.BuildServiceProvider().GetRequiredService<ILogger<Program>>();
builder.Services.AddControllersWithViews(config =>
{
config.Filters.Add(new ActionFilterExample(logger));
});
英文:
I tested your code, and it seems to work well. Do you send your request as JSON with the body?
Could you please verify the Content-Type header?
Here is your code with minor modifications.
class ActionFilterExample:
public class ActionFilterExample : IAsyncActionFilter
{
private readonly ILogger _logger;
public ActionFilterExample(ILogger logger)
{
_logger = logger;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// Log the request details
_logger.LogInformation($"Request: {context.HttpContext.Request.Method} {context.HttpContext.Request.Path}");
// Log the request body for POST requests
if (context.HttpContext.Request.Method.Equals("POST", StringComparison.OrdinalIgnoreCase))
{
context.HttpContext.Request.EnableBuffering();
// seek
context.HttpContext.Request.Body.Seek(0, SeekOrigin.Begin);
// Read the request body
using var reader = new StreamReader(context.HttpContext.Request.Body,
encoding: Encoding.UTF8, detectEncodingFromByteOrderMarks: false, bufferSize: 8192,
leaveOpen: true);
var requestBody = await reader.ReadToEndAsync();
// Reset the position of the request body stream
context.HttpContext.Request.Body.Position = 0;
_logger.LogInformation($"Request Body: {requestBody}");
}
// Call the action method
var resultContext = await next();
// Perform any post-action execution logic if needed
}
inside program.cs
ILogger logger = builder.Services.BuildServiceProvider().GetRequiredService<ILogger<Program>>();
builder.Services.AddControllersWithViews(config =>
{
config.Filters.Add(new ActionFilterExample(logger));
});
答案3
得分: 0
使用内置中间件,考虑到请求中的敏感信息和性能(不将整个请求主体作为字符串缓冲到内存中以记录)。
英文:
Use the built in middleware that takes things like sensitive information in requests and performance into consideration (not buffering the entire request body into memory as a string to log).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论