在ASP.NET Core中记录多个端点的请求,而不需要在每个端点中进行更改。

huangapple go评论68阅读模式
英文:

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语句,我想避免这种情况。基本上,我正在寻找一种通用解决方案。

Link to Stack Overflow Answer

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个可能的问题(目前没有设置进行复制):

  1. EnableBuffering 调用太晚了,请尝试将下一个中间件添加到流水线的开头(就像这里所做的那样):
app.Use((context, next) =>
{
    context.Request.EnableBuffering();
    return next();
});
// ...
  1. 流已经被读取,重新读取传入的流之前将其倒回:
// ...
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):

  1. 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();
});
// ...
  1. 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?

在ASP.NET Core中记录多个端点的请求,而不需要在每个端点中进行更改。

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($&quot;Request: {context.HttpContext.Request.Method} {context.HttpContext.Request.Path}&quot;);

        // Log the request body for POST requests
        if (context.HttpContext.Request.Method.Equals(&quot;POST&quot;, 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($&quot;Request Body: {requestBody}&quot;);
        }

        // 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&lt;ILogger&lt;Program&gt;&gt;();

builder.Services.AddControllersWithViews(config =&gt; 
{
    config.Filters.Add(new ActionFilterExample(logger));
});

在ASP.NET Core中记录多个端点的请求,而不需要在每个端点中进行更改。

答案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).

huangapple
  • 本文由 发表于 2023年6月8日 02:20:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/76426079.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定