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

ASP.NET Core Web API如何统一处理异常替代控制器重复try-catch

最优实现方案:全局异常处理

ASP.NET Core 原生请求管道支持全局异常拦截,完全不需要在每个控制器重复编写try-catch代码,一次配置即可覆盖所有接口、服务层、中间件抛出的未处理异常,冗余度为0。

方案1:自定义异常中间件(兼容所有.NET Core/.NET 5+版本,通用性最强)

核心逻辑是把异常捕获逻辑放到请求管道的最外层,所有后续请求执行过程中抛出的异常都会被统一捕获处理。

  1. 编写全局异常中间件:
public class GlobalExceptionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<GlobalExceptionMiddleware> _logger;

    public GlobalExceptionMiddleware(RequestDelegate next, ILogger<GlobalExceptionMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (UnauthorizedAccessException ex)
        {
            _logger.LogWarning(ex, "未授权访问");
            await WriteResponseAsync(context, StatusCodes.Status401Unauthorized, "访问未授权");
        }
        catch (BadRequestException ex)
        {
            _logger.LogWarning(ex, "请求参数错误");
            await WriteResponseAsync(context, StatusCodes.Status400BadRequest, ex.Message);
        }
        catch (HttpRequestException ex)
        {
            _logger.LogError(ex, "下游HTTP请求失败");
            await WriteResponseAsync(context, StatusCodes.Status503ServiceUnavailable, "外部服务调用失败");
        }
        catch (TimeoutRejectedException ex)
        {
            _logger.LogError(ex, "请求处理超时");
            await WriteResponseAsync(context, StatusCodes.Status408RequestTimeout, "请求处理超时,请稍后重试");
        }
        catch (FileNotFoundException ex)
        {
            _logger.LogError(ex, "资源文件不存在");
            await WriteResponseAsync(context, StatusCodes.Status404NotFound, "请求的资源不存在");
        }
        catch (SqlException ex)
        {
            _logger.LogCritical(ex, "数据库操作异常");
            await WriteResponseAsync(context, StatusCodes.Status500InternalServerError, "服务内部数据处理错误");
        }
        catch (ValidationException ex)
        {
            _logger.LogWarning(ex, "参数校验失败");
            await WriteResponseAsync(context, StatusCodes.Status400BadRequest, ex.Message);
        }
        catch (Exception ex)
        {
            _logger.LogCritical(ex, "未处理的全局异常");
            await WriteResponseAsync(context, StatusCodes.Status500InternalServerError, "服务内部错误,请联系管理员");
        }
    }

    private async Task WriteResponseAsync(HttpContext context, int statusCode, string message)
    {
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = statusCode;
        // 可替换为项目自定义的统一响应结构
        var result = JsonSerializer.Serialize(new { code = statusCode, msg = message, success = false });
        await context.Response.WriteAsync(result);
    }
}
  1. 注册中间件
    在Program.cs中,将异常中间件注册到所有需要捕获异常的中间件最前面,保证异常能被拦截:
var app = builder.Build();

// 全局异常中间件必须放在最前面
app.UseMiddleware<GlobalExceptionMiddleware>();

// 后续再注册其他中间件,例如HTTPS重定向、路由、授权、控制器映射等
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();

方案2:.NET 7+ 原生IExceptionHandler(轻量方案,官方推荐)

.NET 7及以上版本提供了原生异常处理器抽象,不需要手动编写完整中间件,实现接口即可完成全局异常处理:

  1. 编写自定义异常处理器
public class CustomExceptionHandler : IExceptionHandler
{
    private readonly ILogger<CustomExceptionHandler> _logger;
    public CustomExceptionHandler(ILogger<CustomExceptionHandler> logger)
    {
        _logger = logger;
    }

    public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken)
    {
        var (statusCode, msg) = exception switch
        {
            UnauthorizedAccessException => (StatusCodes.Status401Unauthorized, "访问未授权"),
            BadRequestException ex => (StatusCodes.Status400BadRequest, ex.Message),
            HttpRequestException => (StatusCodes.Status503ServiceUnavailable, "外部服务调用失败"),
            TimeoutRejectedException => (StatusCodes.Status408RequestTimeout, "请求处理超时,请稍后重试"),
            FileNotFoundException => (StatusCodes.Status404NotFound, "请求的资源不存在"),
            SqlException => (StatusCodes.Status500InternalServerError, "服务内部数据处理错误"),
            ValidationException ex => (StatusCodes.Status400BadRequest, ex.Message),
            _ => (StatusCodes.Status500InternalServerError, "服务内部错误,请联系管理员")
        };

        _logger.LogError(exception, "全局异常捕获:{Message}", msg);
        httpContext.Response.StatusCode = statusCode;
        httpContext.Response.ContentType = "application/json";
        await httpContext.Response.WriteAsJsonAsync(new { code = statusCode, msg = msg, success = false }, cancellationToken);
        return true;
    }
}
  1. 注册服务和中间件
// Program.cs中注册服务
builder.Services.AddExceptionHandler<CustomExceptionHandler>();

var app = builder.Build();
// 注册全局异常中间件
app.UseExceptionHandler(_ => { });

// 其他中间件注册...

注意事项

  • 配置完成后可以直接删除所有控制器中冗余的try-catch代码,全局处理器会自动捕获所有未处理异常,覆盖范围包含控制器、服务层、其他自定义中间件,比控制器内单独捕获的覆盖范围更广
  • 异常中间件必须放在请求管道的最前端,否则后续中间件抛出的异常无法被捕获
  • 特殊接口需要自定义异常返回逻辑时,可以在接口内部单独编写try-catch,局部捕获的异常不会进入全局处理逻辑,兼容特殊场景需求
  • 不推荐使用早期MVC版本的ExceptionFilter方案,该方案只能捕获控制器层的异常,无法拦截中间件、路由层等其他位置抛出的异常,覆盖范围有限

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 01:27:16