ASP.NET Core Web API异常处理方案咨询:两种主流方式解析
ASP.NET Core Web API 异常与意外结果处理方案探讨
尽管这是个偏主观的问题,但我还是想了解大家在ASP.NET Core Web API中处理异常或意外结果时的常用方案。目前我了解到两种主流思路,说不定还有更优的选择:
1. 结果模式
异常不在控制器层处理,而是由服务层统一处理。控制器方法签名为ActionResult<Result<T>>,其中T代表资源/数据对象,Result<T>是一个包装类,包含Errors、ResultType等附加字段来标识结果状态。
示例代码:
// BlogPostsController.cs ... private readonly IBlogPostsService _service; [HttpGet] public async Task<ActionResult<Result<BlogPost>>> GetPostById(Guid postId) { var result = await _service.GetPostById(postId); return result.ResultType switch { ResultType.NotFound => NotFound(result), ResultType.Ok => Ok(result), ResultType.Unexpected => BadRequest(result) }; }
2. 自定义异常+全局中间件模式
异常不在控制器层处理,由服务层根据业务场景抛出自定义异常,再通过全局中间件统一捕获并处理,将异常转换为标准的HTTP响应。
控制器代码
// BlogPostsController.cs ... private readonly IBlogPostsService _service; [HttpGet] public async Task<IActionResult> GetPostById(Guid postId) { return await _service.GetPostById(postId); }
服务层代码
// BlogPostsService.cs public async Task<BlogPost?> GetPostById(Guid postId) { var post = await _db.Where(post => post.Id == postId).SingleOrDefaultAsync(); if (post == null) throw new PostNotFoundException($"Post {postId} not found"); return post; }
异常处理中间件代码
// ExceptionMiddleware.cs public async Task Invoke(HttpContext context) { try { await _next(context); } catch (PostNotFoundException) { var details = new ExceptionDetails() { Status = 404, Detail = "Post does not exist" }; await context.Response.WriteAsync(JsonConvert.SerializeObject(details)); } // 可添加更多自定义异常的处理分支 ... }
补充方案:自定义异常过滤器
除了中间件,还可以使用ASP.NET Core自带的异常过滤器(IExceptionFilter或IAsyncExceptionFilter)处理异常。过滤器更贴近MVC管道,能直接访问ActionContext,支持针对特定控制器或Action配置,灵活性更强。
示例代码:
public class CustomExceptionFilter : IAsyncExceptionFilter { private readonly ILogger<CustomExceptionFilter> _logger; public CustomExceptionFilter(ILogger<CustomExceptionFilter> logger) { _logger = logger; } public async Task OnExceptionAsync(ExceptionContext context) { _logger.LogError(context.Exception, "未处理的异常发生"); if (context.Exception is PostNotFoundException) { context.Result = new NotFoundObjectResult(new { Status = 404, Detail = "请求的文章不存在" }); } else { context.Result = new ObjectResult(new { Status = 500, Detail = "服务器发生意外错误" }) { StatusCode = StatusCodes.Status500InternalServerError }; } context.ExceptionHandled = true; } }
注册过滤器到MVC管道:
// Program.cs builder.Services.AddControllers(options => { options.Filters.Add<CustomExceptionFilter>(); });
各方案适用场景总结
- 结果模式:适合需要严格控制返回结果结构、避免依赖异常抛出的场景,逻辑更显式,但会增加控制器层的判断代码,且服务层需要维护Result对象的状态。
- 自定义异常+中间件/过滤器:适合将异常处理逻辑集中管理,控制器代码更简洁,符合关注点分离原则;但需注意不要过度定义自定义异常,避免类型泛滥,可按业务领域或错误类型归类。
内容的提问来源于stack exchange,提问作者tenticon
相关产品推荐
相关产品推荐

