简体   繁体   中英

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 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

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 .

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

在此处输入图像描述

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM