.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。
我最近将 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。