thinkphp中间件执行顺序由路由注册顺序决定,全局中间件在所有路由中间件之后执行(响应阶段相反),分组中间件嵌套在对应路由内,依赖关系需按实际执行层级安排。

中间件执行顺序由路由注册顺序决定,不是配置文件里写几行就能改的
ThinkPHP 的中间件执行顺序完全取决于它被绑定到路由时的先后位置,全局中间件和分组中间件混用时尤其容易误判。很多人翻遍 middleware.php 和 app/middleware.php,以为改数组顺序就行——其实那只是「中间件类定义列表」,不参与执行调度。
真正起作用的是你在 app/route.php 里调用 middleware() 的时机和位置。比如:
// 正确:先注册 AuthMiddleware,再注册 LogMiddleware → 执行时 Auth 在前、Log 在后
Route::group('api', function () {
Route::get('user', 'Api/User/index');
})->middleware(AuthMiddleware::class)->middleware(LogMiddleware::class);
// 错误:顺序反了,Log 就会比 Auth 先执行(可能连用户都没鉴权就记日志了)
Route::group('api', ...)->middleware(LogMiddleware::class)->middleware(AuthMiddleware::class);
-
middleware()是链式调用,越靠前的越先执行(请求阶段),越靠后的越后执行(响应阶段) - 全局中间件走
app/middleware.php中的global数组,但它的执行时机固定在所有路由中间件之后(响应阶段则相反) - 如果某个中间件依赖另一个中间件设置的请求属性(如
$request->userInfo),就必须确保它排在依赖项之后
全局中间件 vs 分组中间件:执行层级不同,不能简单对比顺序
全局中间件在「整个请求生命周期最外层」运行,分组中间件则嵌套在对应路由匹配逻辑内。这意味着即使你把 CheckToken 放在全局,而 Auth 放在 admin 分组里,实际执行顺序仍是:全局 → 分组 → 控制器。但反过来,分组中间件无法影响全局中间件已处理过的请求状态。
常见错误现象:Auth 中间件从 $request 取不到 token,因为前面的全局 ParseJsonBody 没跑,或者跑得太晚。
- 全局中间件只对所有 HTTP 请求生效,不区分路由;分组中间件只对本组内匹配的 URL 生效
- 全局中间件无法访问路由参数(
$route->param()),因为它在路由解析前就执行了 - 若需「先解析 body、再鉴权、再进分组逻辑」,必须把
ParseJsonBody放全局,Auth放分组,且保证分组中间件注册在全局之后
使用 append() / prepend() 动态调整中间件顺序要小心
ThinkPHP 提供了 Route::append() 和 Route::prepend() 方法,看起来能插队,但它们只影响「当前路由规则」的中间件链,不会改变已注册的全局或分组中间件顺序。而且一旦用了这些方法,后续再调用 middleware() 链式调用,行为就难预测了。
典型踩坑:在 app/route.php 开头写了 Route::prepend(CorsMiddleware::class),结果发现某些接口 CORS 头没生效——因为那些接口是通过 Route::rule() 单独注册的,没走 group 或 resource,压根不受 prepend 影响。
-
prepend()只对后续所有「未指定中间件」的路由规则生效,已有middleware()调用的路由不会叠加 - 动态添加中间件时,
append()插入到当前链尾部(响应阶段最后执行),prepend()插入到头部(请求阶段最先执行) - 生产环境建议避免混合使用
prepend()/append()和链式middleware(),容易漏掉调试痕迹
调试中间件执行顺序最靠谱的方式是打日志 + 看 trace
光看代码排列猜顺序很容易错,尤其当项目引入了多个扩展包(比如 think-swoole、think-api)自动注册中间件时。最直接的办法是在每个中间件的 handle() 开头加一行日志,再配合内置 trace 工具确认实际执行流。
示例:在 app/middleware/AuthMiddleware.php 中:
public function handle($request, \Closure $next)
{
\think\facade\Log::info('AuthMiddleware: start');
$response = $next($request);
\think\facade\Log::info('AuthMiddleware: end');
return $response;
}
- 日志时间戳比代码顺序更可信,特别是 Swoole 环境下中间件可能复用 Request 对象
- 开启 trace 后,在浏览器开发者工具 Network 标签页查看响应头中的
X-Trace-ID,再查日志就能串起完整链路 - 注意:中间件中不要用
dump()或exit,会导致响应中断,trace 信息不全
中间件顺序问题本质是请求生命周期的嵌套控制,不是配置开关。最容易被忽略的是:你以为自己在调序,其实只是没看清哪个中间件属于哪一层、何时被加载、是否被条件跳过。多打两行日志,比翻文档快得多。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










