You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core中FastEndpoints全场景日志覆盖方案咨询

问题背景

我们有一个已上线的大型ASP.NET Core项目,使用的FastEndpoints组件版本如下:

<PackageReference Include="FastEndpoints" Version="5.20.0" />
<PackageReference Include="FastEndpoints.Swagger" Version="5.20.0" />

项目包含50+基于FastEndpoints开发的端点,当前通过集中式日志中间件执行端点逻辑:

await logger.ExecuteAndLogRequest(
             request,
             () => ExecuteRequestAsync(request, cancellationToken), //endpoint method
             cancellationToken);

存在两个核心问题:

  1. 该日志逻辑仅在端点返回EndpointResponse时记录请求信息,而50%的端点使用ThrowError()、SendErrorsAsync()等FastEndpoints原生方法,这类请求无返回值,导致日志缺失。
  2. 新增ErrorHandlerLogger中间件捕获4xx及以上状态码请求的日志,但导致正常返回的请求出现日志重复。

需要一套能覆盖所有场景(FastEndpoints验证失败、自定义异常、正常响应)的日志方案,且必须兼容现有已上线的大型项目。

解决方案

方案1:利用FastEndpoints全局事件钩子(推荐)

FastEndpoints 5.x版本提供了全局事件拦截能力,可在端点执行的全生命周期注入日志逻辑,无需修改现有端点代码,侵入性极低,完美兼容现有项目。

实现步骤

  1. 创建全局事件处理类,实现IEndpointEvents接口:
public class GlobalEndpointLogger : IEndpointEvents
{
    private readonly IEndpointLogger _logger;

    public GlobalEndpointLogger(IEndpointLogger logger)
    {
        _logger = logger;
    }

    // 端点执行前(可选,用于记录请求启动信息)
    public Task OnBeforeExecuteAsync(EndpointDefinition endpoint, HttpContext ctx, CancellationToken ct)
    {
        // 可记录请求路径、HTTP方法、请求体等基础信息
        return Task.CompletedTask;
    }

    // 端点执行成功(正常返回EndpointResponse时触发)
    public Task OnAfterExecuteAsync(EndpointDefinition endpoint, HttpContext ctx, object? response, CancellationToken ct)
    {
        // 复用原有日志逻辑,保证格式一致性
        var request = ctx.RequestServices.GetService(endpoint.RequestType);
        if (request != null && response is EndpointResponse)
        {
            _logger.ExecuteAndLogRequest(request, () => Task.FromResult(response), ct).Wait(ct);
        }
        return Task.CompletedTask;
    }

    // 端点执行出错(覆盖ThrowError、SendErrorsAsync、验证失败、未捕获异常等所有错误场景)
    public Task OnErrorAsync(EndpointDefinition endpoint, HttpContext ctx, Exception? ex, CancellationToken ct)
    {
        var request = ctx.RequestServices.GetService(endpoint.RequestType);
        // 获取FastEndpoints原生错误详情
        var errorDetails = ctx.Items[ErrorResponseBuilder.ErrorKey] as ErrorResponse;

        // 执行错误日志记录逻辑,可复用原有日志框架格式
        _logger.LogErrorRequest(request, ctx.Response.StatusCode, errorDetails?.Errors, ex);
        return Task.CompletedTask;
    }
}
  1. 在Program.cs中注册全局事件:
builder.Services.AddFastEndpoints(c =>
{
    c.EndpointEvents.Add<GlobalEndpointLogger>();
});
  1. 移除原有日志中间件的ExecuteAndLogRequest包裹逻辑,以及新增的ErrorHandlerLogger中间件,彻底避免日志重复。

方案2:通过HttpContext标记避免日志重复

如果无法改动全局事件配置,可通过HttpContext.Items标记请求是否已被日志处理,解决重复问题。

实现步骤

  1. 修改原有ExecuteAndLogRequest方法,记录日志后标记请求:
public async Task<TResponse> ExecuteAndLogRequest<TRequest, TResponse>(TRequest request, Func<Task<TResponse>> endpointFunc, CancellationToken ct)
{
    var response = await endpointFunc();
    // 执行原有正常请求日志逻辑
    LogRequest(request, response);
    // 标记该请求已完成日志记录
    HttpContextAccessor.HttpContext.Items["RequestLogged"] = true;
    return response;
}
  1. 修改ErrorHandlerLogger中间件,仅处理未标记的错误请求:
public async Task Invoke(HttpContext context, IEndpointLogger logger)
{
    await _next(context);

    if (context.Response.StatusCode >= 400 && !context.Items.ContainsKey("RequestLogged"))
    {
        var endpointDef = context.GetEndpoint()?.Metadata.GetMetadata<EndpointDefinition>();
        var request = context.RequestServices.GetService(endpointDef?.RequestType);
        // 执行错误日志记录逻辑
        logger.LogErrorRequest(request, context.Response.StatusCode);
    }
}
  1. 确保中间件注册顺序:FastEndpoints中间件 -> ErrorHandlerLogger中间件,保证错误状态码先被FastEndpoints设置完成。

方案3:结合FastEndpoints内置错误响应构建器

利用FastEndpoints原生的ErrorResponseBuilder统一处理验证失败、ThrowError等场景的日志,同时保留正常响应的日志逻辑。

实现步骤

  1. 在Program.cs中配置全局错误响应构建器,注入日志:
builder.Services.AddFastEndpoints(c =>
{
    c.ErrorResponseBuilder = (failures, ctx, statusCode) =>
    {
        var logger = ctx.RequestServices.GetRequiredService<IEndpointLogger>();
        var endpointDef = ctx.GetEndpoint()?.Metadata.GetMetadata<EndpointDefinition>();
        var request = ctx.RequestServices.GetService(endpointDef?.RequestType);
        
        // 记录错误请求日志
        logger.LogErrorRequest(request, statusCode, failures);
        return new ErrorResponse { Errors = failures };
    };
});
  1. 保留原有正常响应的日志逻辑,移除重复的ErrorHandlerLogger中间件。
方案选择建议
  • 优先选方案1:完全利用FastEndpoints原生扩展能力,无需修改现有端点代码,侵入性最低,最适合大型已上线项目。
  • 若无法改动全局事件配置,选方案2:通过标记避免重复,改动量小,兼容现有逻辑。
  • 方案3适合仅需处理FastEndpoints内置错误场景的情况,但需额外处理未捕获的全局异常。

内容的提问来源于stack exchange,提问作者Amv

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 06:08:17