Skip to content

总览

HTTP 请求进入管道后,依次经过一串中间件,每个中间件在 next(context) 调用前后都有执行机会。顺序错了,行为就错。这一篇讲清楚内置了哪些中间件、推荐的排列顺序、以及每一步的依赖关系。

内置中间件

中间件注册方式作用
异常处理host.use<ExceptionHandlerMiddleware>()try-catch 包住下游,未处理异常统一写 500
健康检查host.useHealthChecks("/health")暴露 /health 端点供探活,命中即短路
静态文件host.useFileServer()提供静态资源访问,命中文件即短路
路由匹配host.useRouting()匹配 URL 到 Endpoint,挂到 HttpContext 上
终结点执行host.useEndpoints() / 自动取出 Endpoint.delegate 执行;app.run() 自动补
CORShost.useCors("策略名")处理跨域预检和响应头,读 Endpoint.metadata 中的 CORS 策略
身份认证host.useAuthentication()验 JWT / Cookie,解析出 User 挂到 context.user
身份授权host.useAuthorization()读 Endpoint.metadata 中的权限要求,对 User 验策略
MVChost.mapControllers()扫描 [Route] 控制器,注册为终结点
OpenAPIhost.mapOpenApi() / host.useOpenApiUI()生成 /openapi/v1.json 和 API 文档页

推荐顺序

ExceptionHandlerMiddleware    ← 最外层 try-catch,兜底
useHealthChecks               ← 健康检查,提前短路
useFileServer                 ← 静态文件,命中即短路
useRouting                    ← 匹配 URL,挂 Endpoint
useCors                       ← 跨域处理
useAuthentication             ← 验 token,挂 User
useAuthorization              ← 验权限,放行或拦截
(自定义中间件)                 ← 日志、审计、限流等
(终结点执行)                   ← Endpoint.delegate.invoke

每一步的依赖关系

ExceptionHandlerMiddleware — 最外层兜底

用 try-catch 包住整个下游管道。任何中间件或终结点抛出的未处理异常,穿透回来被 catch 住,统一写成 500 响应。必须在最外层,否则它下游的中间件抛异常就没人接了。

useHealthChecks — 路由之前短路

健康检查端点(/health)不需要走路由匹配,也不需要鉴权。放在路由之前:命中就直接返回 200,next() 不调,后面的路由、认证、授权全部跳过。

useFileServer — 路由之前短路

静态文件(CSS、JS、图片)同样不需要路由。放在路由之前:命中文件直接返回内容,不命中则 next() 继续。

useRouting — 匹配 URL,挂 Endpoint

路由必须在认证和授权之前,因为 useAuthorization 需要读 context.getEndpoint()?.metadata 才知道当前请求需要什么权限。没有路由匹配,Endpoint 就不存在,metadata 也无从读起。

这也是为什么路由中间件和执行中间件拆成两个——留下空档给认证授权。

useCors — 处理跨域

CORS 中间件需要读 Endpoint.metadata 中的 CORS 策略名(由 .requireCors("策略名") 写入),所以放在路由之后。

为什么在授权之前?CORS 预检请求(OPTIONS)不携带 cookie 和 Authorization 头,此时 context.user 必然是空的。如果授权在 CORS 前面,预检请求会被 401 拦掉——但预检的目的就是要知道"能不能跨域访问",应该先让 CORS 处理完了再说。

useAuthentication — 验 token,挂 User

从请求头解析 JWT / Cookie → 构造 ClaimsPrincipal → 写入 context.user。它不拒绝请求——即使没有 token,也只是 context.user 保持匿名状态,把"让不让过"的决定留给下一步。

useAuthorization — 验权限,放行或拦截

context.getEndpoint()?.metadata,找到 IAuthorizeData,用 context.user 去对策略。不满足就短路返回 401 或 403。必须在认证之后(需要 user)、路由之后(需要 endpoint)。

自定义中间件 — 日志、审计、限流

业务特定的横切关注点插在授权之后、执行之前。此时 Endpoint 已确定、User 已解析、权限已通过——信息最完整,做日志或审计最合适。

终结点执行

EndpointMiddleware 读出 endpoint.delegate,执行真正的业务逻辑。这是管道的终点。

一句话总结

路由在最前——后面的中间件要读 Endpoint.metadata
认证在授权前——授权判断依赖 User
CORS 在授权前——预检请求不能先被 401 拦掉
异常处理在最外——兜住所有下游抛出的异常
健康检查和静态文件在路由前——提前短路,节省开销