中间件执行顺序由注册索引决定,非类名或文件名;路由级中间件默认前置插入;安全类中间件须按依赖链严格排序;调试应启用middleware_trace查看响应头X-Middleware-Stack字段。

中间件顺序由注册位置决定,不是类名或文件名
ThinkPHP 不会按字母顺序、文件名或类名排序中间件,只认你写在数组里的**索引顺序**。比如 app/middleware.php 返回的数组:
return [
\app\middleware\Auth::class,
\app\middleware\Logger::class,
\app\middleware\Cors::class,
];执行顺序就是 Auth → Logger → Cors,改类名或文件名完全不影响。
常见错误是把中间件类名写错大小写(如 auth::class),或用了未定义的别名(如 'auth' 却没在 config/middleware.php 的 alias 里映射),结果中间件根本没进队列——它不报错,只是静默跳过。
路由级中间件默认插在最前面,不是追加到末尾
你在路由上用 ->middleware() 添加中间件时,ThinkPHP 6.1+ 默认是**前置插入**(unshift),不是 push 到数组尾部。这意味着:
- 全局注册了
Auth和Logger - 又对某条路由加了
->middleware(RateLimit::class) - 实际执行顺序变成:
RateLimit → Auth → Logger
想把它塞到末尾?必须用 ->appendMiddleware() 替代 ->middleware()。注意:多个 ->middleware() 链式调用时,越靠前写的那个越先执行(请求阶段),别指望后写的能“覆盖”前写的顺序。
安全类中间件必须严格按依赖链排位
校验类中间件(如鉴权、CSRF、HTTPS 强制)不是随便放哪儿都行,它们之间有强依赖关系:
立即学习“PHP免费学习笔记(深入)”;
-
ForceHttpsMiddleware必须最顶上——它不依赖任何 request 属性,且要赶在所有解析和鉴权前跳转 -
ValidatePostSize紧跟其后,防大文件上传绕过后续限制 -
ParseJsonBody或VerifyCsrfToken必须在AuthMiddleware之前,否则$request->post()为空或 token 已被消费 -
AuthMiddleware和权限检查中间件必须连续靠前,不能被语言包、日志等隔开 -
Logger应放在靠后位置——它需要完整上下文(如 controller/action 名),太早执行拿不到
多应用模式下,每个子应用的 app/middleware.php 都得单独检查这个顺序,全局配置不透传。
调试实际执行顺序最靠谱的方法是看响应头
翻代码容易漏掉动态注册、条件加载或路由分组里的中间件。启用 middleware_trace 后,响应头会出现 X-Middleware-Stack 字段,直接列出本次请求真实走过的中间件类名和顺序。
开启方式很简单,在 config/middleware.php 中加入:
'middleware_trace' => true,然后发一次请求,用 curl 或浏览器开发者工具查看响应头即可。注意:该选项仅用于调试,上线前务必关闭。
真正容易被忽略的是 $next($request) 的位置——所有校验类中间件必须把 return $next($request); 放在 handle() 最后,前面的 if 判断失败就直接 return 响应;如果提前调了 $next(),拦截逻辑就彻底失效了。



















