ASP.NET Core 8.0 Web API:如何用IExceptionHandler处理400与404错误?
解决ASP.NET Core 8 Web API中400/404异常处理及调试问题
一、修正异常处理器配置问题
你当前的Program.cs配置存在冲突:app.UseExceptionHandler("/Error")是传统异常页面的用法,会覆盖你注册的GlobalExceptionHandler,导致无法进入调试。同时配置顺序也需要调整,确保异常处理器在管道早期执行。
修正后的Program.cs核心配置:
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); // 注册自定义异常处理器 builder.Services.AddExceptionHandler<GlobalExceptionHandler>(); // 配置ProblemDetails,统一处理框架生成的状态码(如400、404) builder.Services.AddProblemDetails(options => { options.CustomizeProblemDetails = ctx => { // 自定义404响应 if (ctx.ProblemDetails.Status == StatusCodes.Status404NotFound) { ctx.ProblemDetails.Title = "资源未找到"; ctx.ProblemDetails.Detail = "请求的端点或资源不存在"; } // 自定义400响应(含模型验证错误) else if (ctx.ProblemDetails.Status == StatusCodes.Status400BadRequest) { ctx.ProblemDetails.Title = "无效请求"; var modelState = ctx.HttpContext.Features.Get<ModelStateFeature>()?.ModelState; if (modelState != null && modelState.Any(e => e.Value.Errors.Any())) { ctx.ProblemDetails.Detail = string.Join("; ", modelState.SelectMany(e => e.Value.Errors.Select(err => err.ErrorMessage))); } } }; }); var app = builder.Build(); // 启用自定义异常处理器(不要指定/Error路径) app.UseExceptionHandler(); // 启用状态码页面中间件,捕获404等非异常类状态码 app.UseStatusCodePages(); // 开发环境配置 if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } // 路由、授权等中间件(按需添加) app.UseRouting(); app.UseAuthorization(); app.MapControllers(); // 控制器API需添加此配置 app.Run();
二、优化全局异常处理器
默认的GlobalExceptionHandler只处理500错误,可扩展为根据异常类型返回对应状态码,同时在开发环境返回堆栈信息便于调试:
public class GlobalExceptionHandler : IExceptionHandler { private readonly ILogger<GlobalExceptionHandler> _logger; private readonly IWebHostEnvironment _env; public GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger, IWebHostEnvironment env) { _logger = logger; _env = env; } public async ValueTask<bool> TryHandleAsync( HttpContext httpContext, Exception exception, CancellationToken cancellationToken) { _logger.LogError(exception, "异常发生: {Message}", exception.Message); var problemDetails = new ProblemDetails(); // 根据异常类型匹配状态码 switch (exception) { case ValidationException validationEx: problemDetails.Status = StatusCodes.Status400BadRequest; problemDetails.Title = "验证失败"; problemDetails.Detail = string.Join("; ", validationEx.Errors.Select(e => e.ErrorMessage)); break; case FileNotFoundException fileNotFoundEx: problemDetails.Status = StatusCodes.Status404NotFound; problemDetails.Title = "文件未找到"; problemDetails.Detail = fileNotFoundEx.Message; break; default: problemDetails.Status = StatusCodes.Status500InternalServerError; problemDetails.Title = "服务器错误"; // 开发环境返回完整异常信息,生产环境隐藏 problemDetails.Detail = _env.IsDevelopment() ? exception.ToString() : "内部服务器错误"; break; } httpContext.Response.StatusCode = problemDetails.Status.Value; await httpContext.Response.WriteAsJsonAsync(problemDetails, cancellationToken); return true; } }
三、为什么400错误没进入异常处理器?
当你未传递fileName查询参数时,ASP.NET Core的模型验证在到达你的端点之前就已失败,框架会自动生成400响应,默认不会将此异常抛给IExceptionHandler。通过上述AddProblemDetails的配置,就能统一自定义这类框架生成的状态码响应。
四、结论
不需要单独编写两个自定义中间件,通过以下步骤即可统一处理所有异常和状态码:
- 移除冲突的
app.UseExceptionHandler("/Error"),改用app.UseExceptionHandler()启用自定义处理器; - 配置
AddProblemDetails自定义400、404等框架生成的状态码响应; - 启用
UseStatusCodePages捕获非异常类状态码; - 扩展
GlobalExceptionHandler处理业务异常并返回对应状态码。
内容的提问来源于stack exchange,提问作者InquisitiveLad
相关产品推荐
相关产品推荐

