ThinkPHP6中间件是PSR-15兼容的请求处理器链,必须实现MiddlewareInterface并返回Response实例,否则报错;需用$request->session()获取会话,权限校验应前置注册,调试推荐trace()而非dump()。

ThinkPHP6 的中间件不是“插件”或“钩子”,它本质是 PSR-15 兼容的请求处理器链,必须返回 Response 实例,否则会报错或白屏。
中间件类必须实现 ProcessInterface 并正确返回 Response
很多人写完中间件发现没生效,或者直接抛出 TypeError: Return value must be an instance of Psr\Http\Message\ResponseInterface —— 根本原因就是忘了调用 $next->handle($request) 或返回了非 Response 对象。
- 中间件类需 use
Psr\Http\Server\MiddlewareInterface和Psr\Http\Server\RequestHandlerInterface,但更推荐继承think\middleware\BaseMiddleware(自动处理类型提示) - 核心逻辑必须在
handle()方法里,且最终必须 return 一个Response(哪怕只是return $next->handle($request)) - 若想中断流程并返回响应(如权限拒绝),不能用
exit或die,而应构造并返回json(['code'=>403, 'msg'=>'无权限'])或view('error/403')
如何在中间件里做登录态和权限校验
TP6 中间件默认拿不到 Session 或 Auth 实例,因为容器尚未完全初始化;直接调 session('user_id') 可能返回 null,尤其在 CLI 或跨域预检请求中。
- 用
$request->session()->get('user_id')替代全局session()函数,更可靠 - 权限判断建议抽离为独立服务(如
app\service\AuthService),避免中间件耦合 DB 查询;若必须查库,请确保该中间件注册在core后(如app/middleware.php中靠后位置) - 对 API 接口,优先检查 Header 中的
AuthorizationBearer Token,再 fallback 到 Session;注意预检请求(OPTIONS)不带 Cookie,需单独放行
中间件注册顺序决定执行时机,别把鉴权中间件放太后面
TP6 中间件按数组顺序从左到右执行,但响应阶段是倒序。如果把权限中间件放在日志中间件之后,可能日志已记录,但请求却被拦在后面——这不是 bug,是设计使然。
立即学习“PHP免费学习笔记(深入)”;
- 全局中间件(
app/middleware.php)适用于全站统一拦截,比如 CORS、全局日志 - 路由中间件(
route::get(...)->middleware(...))更精准,适合控制某组接口,例如只对api/v1/user/*启用CheckAuth - 若同时用了多个中间件,且其中某个做了重定向(如未登录跳登录页),请确认它在链中足够靠前,否则后续中间件仍会执行(比如数据库操作、缓存写入)
调试中间件时,dump() 和日志容易失效
中间件处于请求生命周期早期,dump() 输出可能被后续中间件或框架本身覆盖;而 Log::info() 若写在未初始化日志驱动的阶段(如 AppInit 前),会静默失败。
- 临时调试可用
file_put_contents(RUNTIME_PATH.'debug.log', print_r($request->url(), true)."\n", FILE_APPEND) - 更稳妥的是用
trace():在中间件开头加trace('auth_middleware_start', 'debug'),配合配置'show_page_trace' => true查看 - 注意:中间件中不要依赖
$this->app或$this->config等未注入属性,TP6 中间件默认不自动注入容器,要用__construct(ContainerInterface $app)显式声明
真正麻烦的不是写中间件,而是搞清它在哪一层被调用、谁给它传了什么、以及返回值不合规时框架不会明确告诉你错在哪一层——多看 think\middleware\AllowCrossDomain 这类内置中间件的源码,比读文档快得多。



















