.NET Core 6 API异常处理异常:过滤器抛出的异常未被中间件捕获
我有一个.NET Core 6 API,在异常处理方面遇到了奇怪的问题:
- 已配置异常中间件,本应捕获API调用中的所有异常(启动时已正确注册)
- 有一个过滤器
MyFilter,用于通用检查,不满足条件时抛出SomeException - 某个控制器
SomeController重写了OnActionExecutionAsync方法,做特定检查,不满足条件时抛出SomeOtherException
相关代码如下:
public class MyFilter : ActionFilterAttribute { public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { if (someCondition) throw new SomeException(); await next(); } } [MyFilter] public class SomeController { public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { await base.OnActionExecutionAsync(context, next); if (someOtherCondition) throw new SomeOtherException(); await next(); } } public class ExceptionMiddleware { private readonly RequestDelegate _next; public ExceptionMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context, IRequestDataManager requestDataManager) { try { await _next(context); } catch (Exception ex) { // 处理异常逻辑 // 预期:当someCondition满足时捕获SomeException // 实际问题: // 1. 若someCondition和someOtherCondition都满足,捕获到的是SomeOtherException而非预期的SomeException // 2. 若someCondition满足但someOtherCondition不满足,不会捕获到任何异常 } } }
具体问题表现
过滤器中抛出SomeException后,执行流程会回到控制器的OnActionExecutionAsync方法并继续执行,异常没有被中间件捕获。调试时能看到过滤器抛出异常,但后续控制器代码仍正常运行。
解决方案
问题出在控制器重写的OnActionExecutionAsync方法上:当调用base.OnActionExecutionAsync(context, next)时,过滤器抛出的异常会被ASP.NET Core的过滤器管道处理,但你没有检查context.Result是否已被设置。当过滤器抛出异常后,ASP.NET Core会自动将异常转换为ObjectResult并赋值给context.Result,此时后续的await next()不应该再执行,否则会覆盖之前的异常结果。
修改控制器的OnActionExecutionAsync方法如下:
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { await base.OnActionExecutionAsync(context, next); // 检查是否已经有结果(比如过滤器抛出异常后生成的错误结果),如果有则直接返回,不再执行后续逻辑 if (context.Result != null) return; if (someOtherCondition) throw new SomeOtherException(); await next(); }
原理说明
ASP.NET Core的过滤器管道中,当某个过滤器抛出异常时,框架会捕获该异常并将其包装成错误结果(比如500 Internal Server Error的ObjectResult),赋值给context.Result。此时如果控制器的OnActionExecutionAsync继续执行await next(),会触发后续的执行流程,甚至覆盖已有的错误结果,导致原本的异常无法被中间件捕获。
通过检查context.Result是否为null,可以判断之前的过滤器管道是否已经产生了结果(包括异常处理后的结果),如果有则直接终止当前方法,避免后续逻辑干扰异常的正常处理。
内容的提问来源于stack exchange,提问作者p.desterlich

