如何配置全局错误处理器以正确捕获自定义HttpResponseException?
全局异常处理配置方案
问题背景
自定义了HttpResponseException异常类:
using Newtonsoft.Json; namespace Services.Errors.Exceptions; public class HttpResponseException : Exception { public int StatusCode { get; private set; } public HttpResponseException(int statusCode , string message) : base(message) { StatusCode = statusCode; } }
实现了基于IExceptionFilter的错误处理器:
using System.Net; using System.Text.Json; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Newtonsoft.Json; using Services.Contracts; using Services.Errors.Exceptions; using JsonSerializer = System.Text.Json.JsonSerializer; namespace Services.Errors.Handler; public class ErrorHandlingMiddleware : IExceptionFilter { public void OnException(ExceptionContext context) { if (context.Exception is HttpResponseException httpResponseMessage) { var error = new ErrorDto() { Code = httpResponseMessage.StatusCode, Description = context.Exception.Message }; context.Result = new JsonResult(error) { StatusCode = (int)httpResponseMessage.StatusCode}; } } }
并在Program.cs中注册:
builder.Services.AddControllers(options => { options.Filters.Add(typeof(ErrorHandlingMiddleware)); });
当前问题:
- 控制器内主动抛出
HttpResponseException时,能返回预期的格式化响应:{ "code": 404, "description": "NotFound" } - 但路由不存在等请求触发的404异常,返回的是普通Exception的原始错误信息:
System.Exception: Services.Errors.Exceptions.HttpResponseException: NotFound
问题原因
IExceptionFilter仅能捕获控制器动作执行阶段抛出的异常,而路由匹配失败、请求管道早期的异常不会经过控制器过滤器,因此无法被当前处理器捕获。
解决方案
将异常处理器改为全局中间件(Middleware),中间件可以拦截整个请求管道中的所有异常,包括管道早期的错误。
1. 修改错误处理器为中间件
using System.Net; using System.Text.Json; using Microsoft.AspNetCore.Http; using Services.Contracts; using Services.Errors.Exceptions; namespace Services.Errors.Handler; public class ErrorHandlingMiddleware { private readonly RequestDelegate _next; public ErrorHandlingMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context) { try { await _next(context); // 处理未触发异常的404等状态码 if (context.Response.StatusCode == (int)HttpStatusCode.NotFound && !context.Response.HasStarted) { var error = new ErrorDto() { Code = 404, Description = "NotFound" }; await WriteErrorResponse(context, error, HttpStatusCode.NotFound); } } catch (Exception ex) { await HandleExceptionAsync(context, ex); } } private async Task HandleExceptionAsync(HttpContext context, Exception exception) { context.Response.ContentType = "application/json"; ErrorDto error; HttpStatusCode statusCode; if (exception is HttpResponseException httpEx) { statusCode = (HttpStatusCode)httpEx.StatusCode; error = new ErrorDto { Code = httpEx.StatusCode, Description = httpEx.Message }; } else { // 处理其他未捕获异常,返回500 statusCode = HttpStatusCode.InternalServerError; error = new ErrorDto { Code = (int)statusCode, Description = "服务器内部错误" }; } context.Response.StatusCode = (int)statusCode; var json = JsonSerializer.Serialize(error); await context.Response.WriteAsync(json); } private async Task WriteErrorResponse(HttpContext context, ErrorDto error, HttpStatusCode statusCode) { context.Response.StatusCode = (int)statusCode; context.Response.ContentType = "application/json"; var json = JsonSerializer.Serialize(error); await context.Response.WriteAsync(json); } }
2. 修改注册方式
在Program.cs中替换原来的过滤器注册,改为注册中间件(需放在app.UseRouting()之后,app.UseAuthorization()和app.MapControllers()之前):
// 移除原过滤器注册代码 // builder.Services.AddControllers(options => // { // options.Filters.Add(typeof(ErrorHandlingMiddleware)); // }); // 注册全局异常处理中间件 app.UseMiddleware<ErrorHandlingMiddleware>(); app.UseAuthorization(); app.MapControllers();
3. 额外扩展
如果需要统一处理所有非200状态码(如400、401等),可在InvokeAsync方法中添加对应状态码的处理逻辑,确保所有错误都返回统一格式响应。
内容的提问来源于stack exchange,提问作者Какосян Владимир
相关产品推荐
相关产品推荐

