如何在 .NET Core API 中记录请求和响应?

问题描述 投票:0回答:1

我想保留 .NET Core API 中某些 API 端点中的请求和响应日志。为此,我在 ASP.NET Core 中创建了一个 ActionFilter。但是,在我的代码中,代码可以记录请求,但在记录响应的情况下,我收到错误“流不可读”。如何解决这个问题?我的做法正确吗?

public class LogRequestResponseActionFilter : IAsyncActionFilter
{
    private readonly ILogger<LogRequestResponseActionFilter> _logger;

    public LogRequestResponseActionFilter(ILogger<LogRequestResponseActionFilter> logger)
    {
        _logger = logger;
    }

    public async Task OnActionExecutionAsync(ActionExecutingContext context,
        ActionExecutionDelegate next)
    {
        var requestLog = new StringBuilder();
        requestLog.AppendLine("Incoming Request:");
        requestLog.AppendLine($"Method: {context.HttpContext.Request.Method}");
        requestLog.AppendLine($"Path: {context.HttpContext.Request.Path}");
        requestLog.AppendLine($"QueryString: {context.HttpContext.Request.QueryString}");
        requestLog.AppendLine($"Headers: {FormatHeaders(context.HttpContext.Request.Headers)}");
        requestLog.AppendLine($"Schema: {context.HttpContext.Request.Scheme}");
        requestLog.AppendLine($"Host: {context.HttpContext.Request.Host}");
        requestLog.AppendLine($"Body: {await ReadBodyFromRequest(context.HttpContext.Request)}");
        _logger.LogInformation("{requestLog}", requestLog.ToString());

        var resultContext = await next();

        if (resultContext.Result != null)
        {
            var responseBodyText = await new StreamReader(context.HttpContext.Response.Body).ReadToEndAsync();

            var responseLog = new StringBuilder();
            responseLog.AppendLine("Outgoing Response:");
            responseLog.AppendLine($"StatusCode: {context.HttpContext.Response.StatusCode}");
            responseLog.AppendLine($"ContentType: {context.HttpContext.Response.ContentType}");
            responseLog.AppendLine($"Headers: {FormatHeaders(context.HttpContext.Response.Headers)}");
            responseLog.AppendLine($"Body: {responseBodyText}");

            _logger.LogInformation("{responseLog}", responseLog.ToString());
        }
    }
    private static string FormatHeaders(IHeaderDictionary headers)
        => string.Join(", ", headers.Select(kvp => $"{{{kvp.Key}: {string.Join(", ", kvp.Value!)}}}"));

    private static async Task<string> ReadBodyFromRequest(HttpRequest request)
    {
        request.EnableBuffering();    
        using var streamReader = new StreamReader(request.Body, leaveOpen: true);
        var requestBody = await streamReader.ReadToEndAsync();
        request.Body.Position = 0;
        return requestBody;
    }
}
.net-7.0 action-filter custom-action-filter
1个回答
0
投票

Response.Body 流不可读或不可查找,它是用于向调用者发送响应的写入流。为了能够在过滤器中读取它,您必须引入一种 hack 并用内存流替换 Body(如这个答案中所示。)

我建议改用内置的 HTTP 日志中间件


builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.All;
    logging.RequestBodyLogLimit = 4096;
    logging.ResponseBodyLogLimit = 4096;
});
© www.soinside.com 2019 - 2024. All rights reserved.