控制器中间件必须通过public static middleware属性显式声明,支持别名或完整类名;TP8要求实现MiddlewareInterface且handle方法须标注返回类型\think\Response,否则报错。

控制器中间件必须通过 middleware 属性声明,不是靠自动扫描
ThinkPHP 不会根据类名、目录或注解自动把某个类识别为控制器级中间件——它只认你在控制器里显式写的 middleware 属性。漏写这行,哪怕中间件类写得再标准,也完全不生效。
常见错误现象:Auth 中间件类已存在、路径正确、handle() 方法也完整,但控制器方法始终没被拦截,请求直接进 action。
-
middleware必须是控制器类的 public static 属性,类型为数组 - 数组元素可以是字符串别名(如
'auth'),但前提是该别名已在app/middleware.php中注册映射 - 也可以直接写完整类名字符串(如
\app\middleware\Auth::class),跳过别名注册步骤 - TP6 和 TP8 都支持这种写法,但 TP8 要求类必须实现
MiddlewareInterface,否则运行时报错
handle() 方法签名和返回值不能错,尤其注意 TP8 的类型约束
TP6 允许宽松写法:public function handle($request, \Closure $next);TP8 强制要求接口实现 + 明确返回类型,否则 PHP 8.1+ 环境直接抛 TypeError。
典型报错:Return value of app\middleware\Auth::handle() must be an instance of think\Response——这就是没加返回类型标注。
立即学习“PHP免费学习笔记(深入)”;
- TP8 必须写:
public function handle($request, \Closure $next): \think\Response - TP6 可省略返回类型,但建议统一加上,避免升级时踩坑
- 第三个参数(如
$name)可选,用于路由传参,但控制器级中间件一般用不到;若用了,必须确保路由定义里有对应传值(如->middleware('auth', ['admin'])) - 必须 返回
$next($request)或提前返回响应(如return response()->fail('no auth')),否则请求挂起无响应
控制器中间件只对当前控制器所有方法生效,但要注意执行顺序
在控制器中定义 middleware 属性后,它会对该控制器下所有 public 方法(包括 __call 捕获的)生效。但它不区分方法名,也没有内置白名单机制。
如果你只想让 edit 和 delete 过中间件,而 index 不过,不能靠控制器级中间件直接实现——得换方案。
- 要么拆成两个控制器(如
PostController和PostSafeController) - 要么改用 路由级中间件,在
route/app.php中单独给特定路由链加->middleware(...) - 要么在中间件内部手动判断当前操作方法:
$request->action(),但耦合度高,不推荐 - TP8 支持中间件分组和条件绑定,但控制器属性本身不支持条件表达式
中间件内修改 $request,控制器能直接读到,但别动 $request->param()
中间件和控制器共享同一个 $request 实例,所以你在中间件里写 $request->user = $user,控制器里 $this->request->user 就能拿到。
但注意:TP8 中 $request->param() 是只读快照,底层已冻结,直接赋值会静默失败或触发警告;想传数据,应使用 $request->withAttribute('key', $value),控制器再用 $request->getAttribute('key') 取。
- TP6 可以直接设属性,如
$request->auth_role = 'admin',控制器里$this->request->auth_role可访问 - TP8 推荐统一走
withAttribute(),兼容性更好,且不会和框架内部属性冲突 - 不要在中间件里调用
$request->session()(TP8 已禁用),要用think\Session门面或注入 Session 对象
handle() 缺少返回类型标注,以及控制器属性没加 static 关键字。



















