ThinkPHP 6+ 中间件必须放在 app/middleware/ 目录下,类名与文件名严格一致,命名空间为 app\middleware,需继承 think\Middleware 或实现 __invoke 方法;全局中间件在 app/middleware.php 中注册,路由中间件通过 middleware() 方法传入;终止请求须返回 Response 实例(如 response() 或 json()),不可用 echo/print_r;执行遵循洋葱模型,顺序为注册顺序的进入与原路返回。

中间件类文件放哪才被自动加载
ThinkPHP 6+ 的中间件必须放在 app/middleware/ 目录下,且类名需与文件名严格一致(如 CheckAuth.php 对应 CheckAuth 类),否则注册后会报 Class "app\middleware\CheckAuth" not found。
- 类必须继承
think\Middleware或直接实现__invoke方法 - 命名空间必须是
app\middleware(不能写成app\Middlewares或其他) - 如果用了多应用模式,路径是
app/{app_name}/middleware/,别漏掉应用名
全局中间件和路由中间件怎么注册
全局中间件在 app/middleware.php 中注册,数组键是类名,值是布尔或配置数组;路由中间件则在定义路由时用 middleware 方法传入。
- 全局注册示例:
['app\middleware\CheckAuth' => true]表示启用;['app\middleware\CheckAuth' => ['except' => ['/login']]是带参数的写法 - 路由中间件要写全类名,比如
Route::get('user', 'User/index')->middleware('app\middleware\CheckAuth') - 注意:全局中间件对所有 HTTP 请求生效(含 CLI),若只想作用于 Web 请求,得在中间件里手动判断
request()->isCli()
中间件里怎么提前终止请求并返回响应
ThinkPHP 中间件不是靠 return 跳出,而是通过返回 Response 实例中断后续流程。直接 return json(...) 或 return redirect(...) 是常见错误写法,会导致“headers already sent”或空白响应。
- 正确做法是调用
response()辅助函数或Response::create()构造实例,例如:return response('Forbidden', 403)->header('Content-Type', 'text/plain'); - 如果用了 JSON 返回,推荐
return json(['code' => 403, 'msg' => 'No access'])—— 这个json()函数内部已封装好Response对象 - 千万别在中间件里 echo/print_r/var_dump,会污染输出缓冲,导致后续响应异常
中间件执行顺序为什么和注册顺序不一致
ThinkPHP 的中间件执行是“洋葱模型”,注册顺序决定外层到内层的包裹关系,但实际执行是先从最外层进入、再逐层深入、最后原路返回。容易误以为“先注册的先执行”,其实它是双向的。
立即学习“PHP免费学习笔记(深入)”;
- 假设注册顺序为
[A, B, C],执行流是:A→B→C→控制器→C→B→A - 所以权限校验类(如
CheckAuth)适合放前面,日志记录类(如LogRequest)适合放后面,才能捕获完整生命周期 - 调试时可在每个中间件的
__invoke开头加trace('enter A'),结尾加trace('leave A'),看日志顺序就清楚了











