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

如何在异常处理器中优雅访问HTTP上下文?现有实现存疑

解决方案

一、关于builder.Run的担忧与优化写法

你当前的实现不存在重复启动应用的问题:

  • UseExceptionHandler会创建一个独立的中间件分支,只有当后续管道抛出未处理异常时,才会进入这个分支执行builder.Run的逻辑,它和Program.cs末尾的app.Run(主管道终结点)完全隔离,二者不会冲突。

不过可以简化写法,无需嵌套builder.Run,直接利用UseExceptionHandler的重载获取异常和上下文:

public static void ConfigureExceptionHandler(this IApplicationBuilder app, ILogger log)
{
    app.UseExceptionHandler(errorApp =>
    {
        errorApp.Run(async context =>
        {
            var exception = context.Features.Get<IExceptionHandlerFeature>()?.Error;
            if (exception == null) return;

            log.LogError(exception, "全局异常捕获");
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;
            context.Response.ContentType = "application/json";
            await context.Response.WriteAsJsonAsync(new { Error = exception.Message });
        });
    });
}

在Program.cs中直接调用:

app.ConfigureExceptionHandler(app.Logger);

如果想更灵活地处理管道流程,也可以用Use替代Run(适合后续还有异常处理分支逻辑的场景):

public static Action<IApplicationBuilder> ExceptionOptions(ILogger log)
    => builder =>
    {
        builder.UseExceptionHandler(errorApp =>
        {
            errorApp.Use(async (context, next) =>
            {
                var exceptionFeature = context.Features.Get<IExceptionHandlerFeature>();
                if (exceptionFeature?.Error != null)
                {
                    log.LogError(exceptionFeature.Error, "未处理异常");
                    context.Response.StatusCode = StatusCodes.Status500InternalServerError;
                    context.Response.ContentType = "application/json";
                    await context.Response.WriteAsJsonAsync(new { 
                        Message = "服务器内部错误",
                        Detail = exceptionFeature.Error.Message
                    });
                }
                await next();
            });
        });
    };

二、ExceptionOptions2无法进入catch块的原因与解决

你的ExceptionOptions2写法是自定义全局异常中间件的常规思路,但无法进入catch块的核心原因是:

  • 你同时使用了UseExceptionHandler,它会优先捕获后续中间件抛出的异常,导致自定义中间件的try/catch无法拿到异常。

如果想改用自定义中间件实现全局异常捕获,需要移除UseExceptionHandler,并确保中间件顺序(必须放在所有业务中间件之前):

public static Action<IApplicationBuilder> ExceptionOptions2(ILogger log)
    => builder => builder.Use(async (context, next) =>
    {
        try
        {
            await next(context);
            
            // 额外处理404等错误状态码
            if (context.Response.StatusCode == StatusCodes.Status404NotFound)
            {
                context.Response.ContentType = "application/json";
                await context.Response.WriteAsJsonAsync(new { Message = "资源未找到" });
            }
        }
        catch (Exception exception)
        {
            log.LogError(exception, "全局异常捕获");
            context.Response.Clear();
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;
            context.Response.ContentType = "application/json";
            await context.Response.WriteAsJsonAsync(new { Error = exception.Message });
        }
    });

在Program.cs中调用时,要放在app.MapControllers()、app.Run()等之前:

app.UseMiddlewareFromDelegate(Delegates.ExceptionOptions2(app.Logger));

关于Postman提示的AllowStatusCode404Response:

  • 404是正常HTTP状态码,不属于未处理异常,所以catch块不会捕获。上面的代码已添加404状态码的处理逻辑;若使用UseExceptionHandler,也可以在异常处理分支中加入状态码检查:
errorApp.Run(async context =>
{
    var exception = context.Features.Get<IExceptionHandlerFeature>()?.Error;
    var statusCode = context.Response.StatusCode;
    
    if (exception != null)
    {
        log.LogError(exception, "异常捕获");
        statusCode = StatusCodes.Status500InternalServerError;
    }
    
    context.Response.StatusCode = statusCode;
    context.Response.ContentType = "application/json";
    var response = exception != null 
        ? new { Error = exception.Message } 
        : new { Message = $"请求失败,状态码:{statusCode}" };
    await context.Response.WriteAsJsonAsync(response);
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 09:10:34