在 ASP.NET Core Web API 开发中,很多同学习惯在每个控制器的方法里手动写 try-catch 来处理异常。这种做法虽然简单直接,但时间一长,问题就来了:代码里到处都是重复的 try-catch,业务逻辑和错误处理混在一起,改起来费劲,测试也不方便。现代 Web 应用其实有更好的办法——全局异常处理。通过中间件(Middleware)集中捕获那些没被处理的异常,控制器就能专心做自己的事,代码也更干净、一致。
问题:控制器里的重复 try-catch
如果每个控制器都自己写 try-catch,会遇到一堆麻烦:
-
代码大量重复:差不多的异常处理逻辑,这里写一遍那里写一遍,看着就烦;
-
职责不清:控制器本该管业务逻辑,结果还得兼顾错误处理和记日志,乱成一团;
-
维护成本高:哪天想改个错误响应的格式,或者加一种异常类型,得把所有控制器翻出来改一遍;
-
响应不一致:不同人写的控制器,可能返回的 HTTP 状态码、错误格式都不一样,前端对接时一脸懵;
-
测试不好写:业务逻辑里嵌着异常处理,单元测试很难把业务单独拎出来测①。
解决方案:全局异常中间件
自己写一个中间件,就能在请求处理的管道里统一拦住所有没被捕获的异常,控制器里完全不用写 try-catch:
public classGlobalExceptionMiddleware
{
privatereadonly RequestDelegate _next;
privatereadonly 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 (Exception ex)
{
_logger.LogError(ex, "发生未处理的异常。");
await HandleExceptionAsync(context, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = exception switch
{
KeyNotFoundException => StatusCodes.Status404NotFound,
UnauthorizedAccessException => StatusCodes.Status401Unauthorized,
_ => StatusCodes.Status500InternalServerError
};
var response = new
{
error = "服务器发生意外错误。",
// 生产环境不要把 exception.Message 直接丢给客户端,防止泄露敏感信息
};
return context.Response.WriteAsJsonAsync(response);
}
}
这个中间件在管道里捕获所有异常,记完日志后,返回一个统一格式的 JSON 错误信息,既安全又规范②。
注册中间件
在 Program.cs 里把中间件加到请求管道靠前的位置,这样它就能覆盖所有请求:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.UseMiddleware<GlobalExceptionMiddleware>(); // 最好放在 UseRouting 之前
app.MapControllers();
app.Run();
这么一配,所有请求都会被异常处理层罩住,真正做到“一次编写,全局生效”③。
这么做的优势
-
代码清爽:控制器里全是业务逻辑,再也看不到 try-catch;
-
响应统一:所有错误都返回同样结构的 JSON,前端处理起来省心;
-
好维护:想改异常处理逻辑,只改一个地方就够了;
-
更安全:生产环境下,内部错误细节不会暴露给用户;
-
便于监控:所有异常都统一记日志,排查问题方便多了④。
结语
在 ASP.NET Core 应用里,异常处理应该作为一个横切关注点(cross-cutting concern)来集中管理,而不是散落在各个控制器里。用全局异常中间件,不仅让代码更好读、更好改,还能确保系统出错时表现一致、安全可靠。这不只是个小技巧,更是专业级 Web API 的基本素养⑤。
参考资料
① Microsoft. Handle errors in ASP.NET Core. https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling
② Andrew Lock. Creating a custom error handler middleware function in ASP.NET Core. https://andrewlock.net/creating-a-custom-error-handler-middleware-function-in-asp-net-core/
③ Microsoft. ASP.NET Core Middleware. https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/
④ OWASP. Error Handling and Logging Cheat Sheet.
⑤ Martin Fowler. Separation of Concerns.

