简介
全局异常拦截是构建健壮企业级应用的关键基础设施,它能统一处理系统中未捕获的异常,提供友好的错误响应,同时记录完整的异常信息。
背景和作用
在 ASP.NET Core 应用中,异常可能在控制器、数据库操作或中间件中发生。如果每个动作方法都手动处理异常(如 try-catch),代码会变得冗长且难以维护。全局异常拦截器解决了以下问题:
- 统一错误处理:集中捕获所有未处理异常,返回标准化的错误响应。
- 标准化响应:符合
RESTful API规范(如RFC 7807 Problem Details)。 - 日志记录:记录异常详情,便于调试和监控。
- 用户体验:返回友好的错误信息,而非默认错误页面或堆栈跟踪。
- 性能优化:减少重复的异常处理代码,提升开发效率。
主要功能
- 捕获异常:捕获控制器、服务层或其他代码中的未处理异常。
- 标准化响应:返回
JSON格式的错误详情(如状态码、错误消息)。 - 日志记录:记录异常信息(包括堆栈跟踪)到日志系统。
- 自定义处理:根据异常类型返回不同状态码或消息(如 400、409、500)。
- 异步支持:兼容
async/await,适合异步操作。 DI集成:通过依赖注入访问服务(如日志、缓存)。
常见实现方式
异常处理中间件(推荐)
在 ASP.NET Core 管道最前端注册一个中间件,捕获所有后续中间件/终结点抛出的异常。
1public class ExceptionHandlingMiddleware 2{ 3 private readonly RequestDelegate _next; 4 private readonly ILogger<ExceptionHandlingMiddleware> _logger; 5 6 public ExceptionHandlingMiddleware(RequestDelegate next, 7 ILogger<ExceptionHandlingMiddleware> logger) 8 { 9 _next = next; 10 _logger = logger; 11 } 12 13 public async Task InvokeAsync(HttpContext ctx) 14 { 15 try 16 { 17 await _next(ctx); 18 } 19 catch (Exception ex) 20 { 21 _logger.LogError(ex, "Unhandled exception"); 22 await HandleExceptionAsync(ctx, ex); 23 } 24 } 25 26 private static Task HandleExceptionAsync(HttpContext ctx, Exception ex) 27 { 28 ctx.Response.ContentType = "application/problem+json"; 29 ctx.Response.StatusCode = ex switch 30 { 31 ArgumentException _ => StatusCodes.Status400BadRequest, 32 KeyNotFoundException _ => StatusCodes.Status404NotFound, 33 _ => StatusCodes.Status500InternalServerError 34 }; 35 36 var problem = new ProblemDetails 37 { 38 Title = ex.Message, 39 Status = ctx.Response.StatusCode, 40 Detail = ctx.Response.StatusCode == 500 ? "请稍后重试或联系管理员" : null, 41 Instance = ctx.Request.Path 42 }; 43 var json = JsonSerializer.Serialize(problem); 44 return ctx.Response.WriteAsync(json); 45 } 46} 47
注册中间件
在 Program.cs(或 Startup.cs)中最早添加:
1app.UseMiddleware<ExceptionHandlingMiddleware>(); 2 3// 或更简洁的扩展方法 4app.UseExceptionHandling(); // 需自行实现 UseExceptionHandling 扩展 5
优势
- 捕获范围最大:包括所有
MVC、Minimal API、静态文件等。 - 性能开销小,代码集中清晰。
- 易于与依赖注入、日志系统联动。
全局异常过滤器(IExceptionFilter / IAsyncExceptionFilter)
如果只想拦截 MVC Controller 的异常,可实现异常过滤器。
1public class GlobalExceptionFilter : IExceptionFilter 2{ 3 private readonly ILogger<GlobalExceptionFilter> _logger; 4 public GlobalExceptionFilter(ILogger<GlobalExceptionFilter> logger) 5 => _logger = logger; 6 7 public void OnException(ExceptionContext context) 8 { 9 var ex = context.Exception; 10 _logger.LogError(ex, "Unhandled exception in controller"); 11 12 var problem = new ProblemDetails 13 { 14 Title = "请求失败", 15 Status = StatusCodes.Status500InternalServerError, 16 Detail = ex.Message 17 }; 18 context.Result = new ObjectResult(problem) 19 { 20 StatusCode = problem.Status 21 }; 22 context.ExceptionHandled = true; 23 } 24} 25
注册过滤器
1services.AddControllers(options => 2{ 3 options.Filters.Add<GlobalExceptionFilter>(); 4}); 5
局限
- 只拦截通过
MVC管道执行的Action抛出的异常。 - 不会捕获例如中间件或
Minimal API的异常。
.NET 7+ 新增的 IExceptionHandler(推荐)
1public class GlobalExceptionHandler : IExceptionHandler 2{ 3 private readonly ILogger<GlobalExceptionHandler> _logger; 4 private readonly IProblemDetailsService _problemDetailsService; 5 6 public GlobalExceptionHandler( 7 ILogger<GlobalExceptionHandler> logger, 8 IProblemDetailsService problemDetailsService) 9 { 10 _logger = logger; 11 _problemDetailsService = problemDetailsService; 12 } 13 14 public async ValueTask<bool> TryHandleAsync( 15 HttpContext httpContext, 16 Exception exception, 17 CancellationToken cancellationToken) 18 { 19 _logger.LogError(exception, "全局异常: {Message}", exception.Message); 20 21 var statusCode = GetStatusCode(exception); 22 var problemContext = new ProblemDetailsContext 23 { 24 HttpContext = httpContext, 25 Exception = exception, 26 ProblemDetails = new ProblemDetails 27 { 28 Title = "服务器错误", 29 Status = statusCode, 30 Detail = httpContext.RequestServices.GetRequiredService<IWebHostEnvironment>().IsDevelopment() 31 ? exception.ToString() 32 : "请稍后再试", 33 Type = $"https://httpstatuses.io/{statusCode}" 34 } 35 }; 36 37 // 添加自定义扩展 38 problemContext.ProblemDetails.Extensions.Add("requestId", httpContext.TraceIdentifier); 39 40 await _problemDetailsService.WriteAsync(problemContext); 41 return true; // 标记为已处理 42 } 43} 44
注册服务:
1builder.Services.AddExceptionHandler<GlobalExceptionHandler>(); 2builder.Services.AddProblemDetails(); // 添加ProblemDetails支持 3 4var app = builder.Build(); 5app.UseExceptionHandler(); // 启用异常处理中间件 6
内置 UseExceptionHandler
ASP.NET Core 自带的异常处理终结点:
1if (!app.Environment.IsDevelopment()) 2{ 3 app.UseExceptionHandler(errorApp => 4 { 5 errorApp.Run(async ctx => 6 { 7 var feature = ctx.Features.Get<IExceptionHandlerFeature>(); 8 var ex = feature?.Error; 9 // 记录日志… 10 ctx.Response.StatusCode = 500; 11 await ctx.Response.WriteAsJsonAsync(new { message = "服务器内部错误" }); 12 }); 13 }); 14} 15else 16{ 17 app.UseDeveloperExceptionPage(); 18} 19
- 优点:无需自定义
Middleware,框架内置。 - 注意:要在其他中间件之前注册,并在生产/开发环境中分开处理。
资源和文档
- 官方文档:
Exception Handling:learn.microsoft.com/en-us/aspne…Problem Details:learn.microsoft.com/en-us/aspne…
《C#.NET 全局异常到底怎么做?最完整的实战指南》 是转载文章,点击查看原文。

