ASP.NET Core请求管道由app.UseXXX()注册顺序天然形成,顺序错误会导致逻辑中断;UseExceptionHandler需在UseRouting之后才能捕获路由异常;HttpContext不可跨await使用,须用context.Items传递数据;中间件执行呈洋葱模型,await next()前后分别处理请求与响应;MapWhen/Map创建独立分支管道,需在分支内重新注册必要中间件。

ASP.NET Core 的请求管道不是“实现出来”的,而是靠 app.UseXXX() 注册顺序天然形成的;你写错顺序,管道就断了、逻辑就漏了——这不是功能没开,是根本没流过去。
UseExceptionHandler 为什么捕获不到 404 或认证失败?
它只捕获自己之后抛出的异常。如果 app.UseExceptionHandler() 写在 app.UseRouting() 前面,那路由匹配失败(比如访问不存在的路径)产生的 404 异常,压根不会经过它。
-
app.UseRouting()负责解析 URL 并匹配端点,它内部可能 throw new InvalidOperationException("No endpoint matches...") -
app.UseAuthentication()在路由之后执行,它若失败(如 token 过期),异常才会流到后面的UseExceptionHandler - 正确顺序必须是:
app.UseRouting()→app.UseAuthentication()→app.UseAuthorization()→app.UseExceptionHandler()→app.MapControllers()
HttpContext 在 async/await 后还能直接用吗?
不能。一旦离开 InvokeAsync(HttpContext context) 方法体,context 就失效了。跨 await 续用会导致 ObjectDisposedException 或静默空引用。
- 异步中间件中所有业务逻辑必须在同一个
InvokeAsync作用域内完成 - 需要跨阶段传递数据时,用
context.Items["key"] = value,这是线程安全且生命周期绑定请求的容器 - 别存
HttpContext引用到静态变量、Task.Run 闭包、Timer 回调里——这些地方恢复执行时 context 早被回收了
自定义中间件里调用 next() 的时机决定行为走向
中间件不是“插件”,它是洋葱皮:请求进来时从外往里走,响应出去时从里往外退。你在 await next() 前后写的代码,分别对应“前置处理”和“后置包装”。
- 前置常用:读取 Header、记录开始时间、校验签名、设置 culture
- 后置常用:写入 X-Response-Time、统一错误格式、压缩 Body、修改 StatusCode
- 不调
await next():请求就此终止,后续中间件和控制器完全收不到——适合做拦截(如 IP 黑名单) - 调了
await next()但没等完就 return:响应已发,后续中间件仍会执行,但 response body 可能已被写满或关闭,再写会抛异常
MapWhen 和 Map 为什么容易误用?
它们是分支管道,不是条件判断语句。一旦进入 MapWhen 分支,就脱离主流程,主流程里的中间件(比如全局日志、CORS)默认不生效。
-
app.MapWhen(ctx => ctx.Request.Path.StartsWithSegments("/api"), branch => { ... }):分支内必须重新注册所需中间件,否则UseAuthentication等全失效 -
app.Map("/health", healthApp => healthApp.Run(...)):这个子管道是独立的,UseExceptionHandler必须在 healthApp 内注册才管用 - 常见坑:把
MapControllers()包进MapWhen却忘了在里面补UseRouting,结果所有 API 都 404
真正难的不是写中间件,而是想清楚“这个逻辑该在哪一层介入、是否影响下游、是否要透传给响应”。管道不是堆砌功能的地方,是控制流经路径的阀门——拧错一个,整条链就偏了。


















