useexceptionhandler 必须位于 userouting 之后、useendpoints 之前,否则无法捕获路由匹配后但终结点前的异常;它不处理静态资源404、后台任务或线程外异常,需与iexceptionfilter分层协作,并严格按环境返回安全的problemdetails响应。

UseExceptionHandler 必须放在 UseRouting 之后、UseEndpoints 之前,否则 404 或静态资源异常根本进不来——这不是可选项,是执行顺序硬约束。
UseExceptionHandler 为什么注册位置错了就失效
它只捕获 HTTP 请求管道中「已进入路由匹配但尚未抵达终结点」阶段抛出的异常。如果放在 UseStaticFiles 前面,连静态文件 404 都不会触发;如果放在 UseEndpoints 后面,异常早被框架内部吞掉或转成 500 页面。
- 正确顺序示例:
app.UseRouting()→app.UseAuthentication()→app.UseAuthorization()→app.UseExceptionHandler("/error")→app.UseEndpoints(...) - 常见错位:把
UseExceptionHandler放在UseStaticFiles后面却没意识到静态资源路径错误(如 /favicon.ico 不存在)会直接 404,不走 MVC 路由,自然也绕过该中间件 - 开发时用
UseDeveloperExceptionPage可以辅助验证顺序是否生效:如果黄页能出来,说明中间件链没断;如果直接看到 IIS/HTTP 500 页面,大概率是顺序或路径配置问题
IExceptionFilter 和 UseExceptionHandler 到底谁管什么
IExceptionFilter 只对 Controller 的 Action 执行过程中抛出的异常有效,模型绑定失败、授权拒绝、自定义中间件里崩了,它都收不到;UseExceptionHandler 是兜底,覆盖所有中间件和终结点漏掉的异常,但拿不到 ActionContext,没法做细粒度响应定制。
- 两者可以共存:用
IExceptionFilter处理业务明确的异常(比如BusinessException返回 400),再靠UseExceptionHandler拦住NullReferenceException这类运行时崩溃 - 别指望
IExceptionFilter拦住ModelState.IsValid == false:这是框架内置的验证流程,不抛异常,IExceptionFilter根本不触发 - 在
OnException里必须设context.ExceptionHandled = true,否则异常继续上抛,最终还是落到UseExceptionHandler或默认 500 页面
后台任务、Timer、HostedService 的异常完全不经过 UseExceptionHandler
这些代码运行在独立线程或 Task 上,不在 HTTP 上下文里。UseExceptionHandler 的 HttpContext 是空的,它压根看不到这些异常——不是“没捕获”,而是“根本没机会看”。
-
Task.Run(() => { throw new Exception(); })不会触发任何全局中间件,除非你显式.Wait()或.GetAwaiter().GetResult() -
async void方法(如Timer回调)里的异常无法被外层 catch,必须在方法体内try/catch,并手动记录日志 -
IHostedService.StartAsync中的异常会导致宿主启动失败,但不会进UseExceptionHandler;需在方法内包一层try/catch,且不能只吞掉——至少要写日志,否则静默失败 - 未观察的 Task 异常会触发
TaskScheduler.UnobservedTaskException,但 .NET 6+ 默认不终止进程,容易掩盖问题,建议开启ThrowUnobservedTaskExceptions配置(仅限开发环境)
生产环境返回 ProblemDetails 时最常踩的坑
直接 new JsonResult(new { ... }) 看似简单,实则绕过框架安全机制,可能把数据库连接字符串、物理路径、内部类名等敏感信息打到前端。
- 务必用
ProblemDetails类型构造响应体,它是 RFC 7807 标准,框架默认会过滤敏感字段 - 开发环境可保留
Detail字段(含堆栈),生产环境必须清空:Detail = null或设为固定提示语 - 不要依赖
HttpContext.Features.Get<iexceptionhandlerfeature>().Error</iexceptionhandlerfeature>后直接序列化整个异常对象——Error.ToString()就可能泄露信息 - 若用
UseExceptionHandler("/error"),确保对应 Action 返回的是ObjectResult(如Ok(new ProblemDetails {...})),不是ViewResult,否则 API 客户端收到 HTML,解析失败
真正难的不是写一个 try/catch,而是分清异常发生在哪一层、该由谁来负责、以及怎么避免把不该暴露的信息塞进响应体。顺序、线程上下文、环境开关,三者缺一不可。










