中间件不执行的主因是未进入请求链,首要排查app/middleware.php是否存在且返回带命名空间的类名数组,路由绑定须显式调用middleware(),handle()每条分支均需return Response实例。

中间件不执行,八成是它根本没进请求链——不是逻辑写错了,是注册、绑定或返回值断在了最底层。
app/middleware.php 文件不存在或返回非数组
这是第一道关卡。ThinkPHP 6+ 只认这个文件作为全局中间件注册入口,删了、改名了、或者里面写了 echo 或 BOM 头,都会导致框架加载时解析失败,静默跳过整个中间件链。
- 检查文件是否存在:
app/middleware.php,不能是middleware.php.bak或其他名字 - 文件内容必须以
<?php开头,且只返回一个数组,例如:return [\app\middleware\Stats::class]; - 禁止出现任何输出:
echo、var_dump、空格、BOM(用编辑器转 UTF-8 无 BOM 格式) - 多应用模式下(
app/multi_app = true),得去对应应用目录找,比如app/admin/middleware.php,根目录的不生效
路由或控制器没显式调用 middleware()
全局注册 ≠ 全局生效。如果你的目标只是某组 API、某个后台模块,光在 app/middleware.php 里写上类名,对 Route::group('api', ...) 或 Route::resource() 是无效的。
- 分组路由必须手动挂载:
Route::group('api', function () { ... })->middleware(\app\middleware\Stats::class); - 资源路由不会继承控制器构造函数里的
$this->middleware(),每个动作(index/show)都要单独指定,或改用分组绑定 - 闭包路由、
Route::get('user', function () { ... })同样需要链式调用->middleware(...) - 字符串形式的中间件名(如
'stats')需在app/middleware.php中提前注册别名,否则会报错 Class not found
handle() 方法没 return Response 实例
这是最隐蔽的坑:中间件函数末尾漏掉 return,PHP 默认返回 null,框架会当成「中断请求」处理——你的统计代码可能执行了,但后续中间件和控制器全被跳过,日志里也看不到明显报错。
立即学习“PHP免费学习笔记(深入)”;
-
handle($request, \Closure $next)签名必须严格匹配,不能少参数 - 放行必须写:
return $next($request);,只写$next($request)不行 - 拦截必须返回响应实例:
return json(['code' => 401]);或return redirect('/login');,不能用die、exit或echo - 若需传参(如角色名),注册时写
->middleware(\app\middleware\AuthCheck::class, 'admin'),handle()就能多接一个参数:handle($request, \Closure $next, $role)
中间件执行顺序或生命周期踩坑
有些中间件“看似执行了”,实则拿不到数据——不是没跑,是跑得太早或太晚。
- Session 和 POST 数据依赖后续中间件初始化,过早访问
session('user_id')或$request->param()可能为空;优先用$request->post()或\think\facade\Session::get() - 多语言中间件(
LoadLangPack)必须在SessionInit之后、路由调度之前,否则读不到cookie('think_lang') - 想看某次请求实际执行了哪些中间件?开启
middleware_trace => true,响应头里会有X-Middleware-Stack字段,直接列出完整执行链 - 路由级中间件默认前置插入,不是追加到末尾;想强制追加,用
->appendMiddleware()
真正卡住排查的,往往不是逻辑复杂度,而是注册路径不对、命名空间少个反斜杠、或者 handle() 最后一行忘了写 return——这些地方一错,中间件就彻底隐身。



















