.NET Core 3 中的日志请求负载

Log request payloads in .NET Core 3

我最近将 API 升级到 .NET Core 3,据我所知,升级后此位停止工作。

我想记录异常和通过请求正文传入的 json 负载。此 ExceptionLogger 作为过滤器插入以全局处理错误:

public class ExceptionLogger
{
    private readonly IServiceScopeFactory _factory;

    public ExceptionLogger(IServiceScopeFactory factory)
    {
        _factory = factory;
    }   

    public async Task<Guid?> LogException(Exception ex)
    {
        using var scope = _factory.CreateScope();
        var accessor = scope.ServiceProvider.GetRequiredService<IHttpContextAccessor>();
        var displayUrl = accessor?.HttpContext?.Request?.GetDisplayUrl();
        var payload = await RetrievePayload(accessor, displayUrl);

        // ... code proceeds to log the exception, including the payload.
    }

    private async Task<string> RetrievePayload(IHttpContextAccessor accessor, string displayUrl)
    {
        accessor.HttpContext.Request.EnableBuffering();
        accessor.HttpContext.Request.Body.Seek(0, System.IO.SeekOrigin.Begin);
        using var sr = new System.IO.StreamReader(accessor.HttpContext.Request.Body);

        return await sr.ReadToEndAsync(); // The request body is always empty now....!
    }
}

accessor 有查询字符串和表单参数,甚至 accessor.HttpContext.Request.ContentLength 也有一个值表明有效载荷在那里。但是,Request.Body 流是空的,即使在我重置光标位置之后也是如此。

此时如何获取请求正文?

这是 .net core 3.0 的问题。如果您在为此配置它之前尝试多次读取该主体,它将在第一次读取时成功,而随后的读取将失败。因此,您需要一个中间件来配置该主体以进行多次读取。如前所述 here there was a work around for that marking request with request.EnableRewind() and as mentioned here 您需要将对 EnableRewind 的任何调用更改为 EnableBuffering。因此,如果我们 return 您的情况,您的错误处理程序应该如下所示:

    public class ErrorLoggingMiddleware
{
    private readonly RequestDelegate next;

    public ErrorLoggingMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            context.Request.EnableBuffering(); // this part is crucial
            await this.next(context);
        }
        catch (Exception e)
        {

            var stream = context.Request.Body;
            stream.Seek(0, SeekOrigin.Begin);
            using var reader = new StreamReader(stream);
            var bodyString =  await reader.ReadToEndAsync();


            // log the error
        }
    }
}

或者您可以先将您的中间件分成两部分,以便像第一个 link 中提到的那样倒带。因为它必须在 运行 之前先读取到正文。 (需要先在StartUp.cs配置中注册)然后你可以注册你的错误记录器中间件或者你也可以使用过滤器,因为我们已经为多次读取启用了body。