using NetAdmin.Host.Utils;
namespace NetAdmin.Host.Middlewares;
///
/// 请求审计中间件
///
///
/// 放在所有中间件最前面
///
public sealed class RequestAuditMiddleware(
RequestDelegate next
, IOptions dynamicApiControllerSettingsOptions
, RequestLogger requestLogger)
{
private readonly PathString _defaultRoutePrefix
= new($"/{dynamicApiControllerSettingsOptions.Value.DefaultRoutePrefix}");
private readonly PathString _healthCheckRoutePrefix
= new($"/{dynamicApiControllerSettingsOptions.Value.DefaultRoutePrefix}/health/check");
///
/// 主函数
///
public async Task InvokeAsync(HttpContext context)
{
// 跳过处理的情况:
if (!context.Request.Path.StartsWithSegments(_defaultRoutePrefix) // 非api请求
|| context.Request.Path.StartsWithSegments(_healthCheckRoutePrefix) // 健康检查
|| context.Request.Method == Chars.FLG_HTTP_METHOD_OPTIONS) { // is options 请求
await next(context).ConfigureAwait(false);
return;
}
// Response.Body流默认是不可读的,将Response.Body流替换成MemoryStream 使其可读
await using var ms = new MemoryStream();
var stream = context.Response.Body;
context.Response.Body = ms;
// 在控制台上输出分割线,区分不同请求
// 调用下一个中间件
var sw = Stopwatch.StartNew();
await next(context).ConfigureAwait(false);
sw.Stop();
_ = ms.Seek(0, SeekOrigin.Begin);
using var sr = new StreamReader(ms);
var responseBody = await sr.ReadToEndAsync().ConfigureAwait(false);
_ = ms.Seek(0, SeekOrigin.Begin);
await ms.CopyToAsync(stream).ConfigureAwait(false);
context.Response.Body = stream;
var exception = context.Features.Get();
var errorCode = context.Response.Headers[nameof(ErrorCodes)] //
.FirstOrDefault()
?.Enum() ?? 0;
_ = await requestLogger.LogAsync(context, (long)sw.Elapsed.TotalMicroseconds, responseBody, errorCode
, exception)
.ConfigureAwait(false);
}
}