.NET 7最小API:IExceptionFilter注册及全局异常处理问题
最小API全局异常处理解决方案
最小API不像MVC控制器体系那样支持IExceptionFilter注册,下面提供三种实用方案解决你的问题:
方案一:自定义全局异常中间件(推荐)
这是最灵活且不受环境限制的方式,能捕获所有请求的异常,调试/生产模式均生效。
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 (Exception ex) { _logger.LogError(ex, "未处理的全局异常"); await HandleExceptionResponse(context, ex); } } private static async Task HandleExceptionResponse(HttpContext context, Exception ex) { context.Response.ContentType = "application/json"; // 根据异常类型自定义状态码 context.Response.StatusCode = ex switch { ArgumentException => StatusCodes.Status400BadRequest, KeyNotFoundException => StatusCodes.Status404NotFound, _ => StatusCodes.Status500InternalServerError }; // 构造自定义响应体,调试模式可返回详细信息,生产模式可隐藏 var response = new { StatusCode = context.Response.StatusCode, Message = context.Response.StatusCode == 500 ? "服务器内部错误" : ex.Message, DebugDetail = Environment.IsDevelopment() ? ex.ToString() : null }; await context.Response.WriteAsync(System.Text.Json.JsonSerializer.Serialize(response)); } }
2. 注册中间件到管道
在Program.cs中,确保中间件放在管道最靠前的位置(早于路由、端点注册):
var builder = WebApplication.CreateBuilder(args); // 注册服务... var app = builder.Build(); // 注册全局异常中间件 app.UseMiddleware<GlobalExceptionMiddleware>(); app.UseRouting(); // 其他中间件、路由注册... app.MapGet("/api/test", () => { throw new InvalidOperationException("仓储层测试异常"); }); app.Run();
方案二:改造UseExceptionHandler使其调试模式生效
默认UseExceptionHandler在开发模式下会被开发者异常页面覆盖,只需禁用开发者异常页面即可让它接管所有环境的异常处理:
var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); // 注释或移除开发者异常页面,让UseExceptionHandler生效 // app.UseDeveloperExceptionPage(); app.UseExceptionHandler(errorApp => { errorApp.Run(async context => { context.Response.ContentType = "application/json"; var exceptionFeature = context.Features.Get<IExceptionHandlerPathFeature>(); var ex = exceptionFeature?.Error; context.Response.StatusCode = ex != null ? StatusCodes.Status500InternalServerError : StatusCodes.Status404NotFound; var response = new { Path = exceptionFeature?.Path, Message = ex?.Message ?? "请求路径不存在", StatusCode = context.Response.StatusCode }; await context.Response.WriteAsync(System.Text.Json.JsonSerializer.Serialize(response)); }); }); // 路由、端点注册... app.MapGet("/api/test", () => { throw new Exception("仓储层抛出异常"); }); app.Run();
方案三:端点过滤器(针对单个/批量端点)
如果只需处理特定端点的异常,可使用端点过滤器:
单个端点添加过滤器
app.MapGet("/api/test", () => { throw new InvalidOperationException("测试异常"); }) .AddEndpointFilter(async (context, next) => { try { return await next(context); } catch (Exception ex) { return Results.Json(new { Message = ex.Message }, statusCode: 500); } });
全局注册端点过滤器
对所有最小API端点生效:
builder.Services.Configure<RouteHandlerOptions>(options => { options.AddEndpointFilter(async (context, next) => { try { return await next(context); } catch (Exception ex) { return Results.Json(new { StatusCode = 500, Message = "服务器处理失败", Detail = Environment.IsDevelopment() ? ex.Message : null }); } }); });
内容的提问来源于stack exchange,提问作者Gargoyle
相关产品推荐
相关产品推荐

