必须用 $request->withattribute() 传值,因 thinkphp 的 $request 不可变;控制器须用 getattribute() 取值,且 key 大小写敏感。

中间件里用 $request->withAttribute() 才能传值给控制器
ThinkPHP 的 $request 对象是不可变的(immutable),直接给它加属性(比如 $request->user_id = 123)不会生效,控制器里读不到。必须用 PSR-7 标准的 withAttribute() 方法创建新实例。
- 错误写法:
$request->user_id = 123或$request['user_id'] = 123—— 属性不持久,控制器里$this->request->user_id是null - 正确写法:
$request = $request->withAttribute('user_id', 123),然后return $next($request, $response) - 注意:必须把新
$request传给$next(),否则下游收不到 - 多个值就多次调用:
$request->withAttribute('role', 'admin')->withAttribute('scope', ['read'])
控制器里用 $this->request->getAttribute() 取值,别用动态属性
很多人习惯写 $this->request->user_id,但在 TP6+ 中这会触发魔术方法 __get(),但默认不代理到 attribute,结果是 Notice: Undefined property 或返回 null。
- 安全取值方式只有:
$this->request->getAttribute('user_id')(返回null如果没设) - 带默认值可写:
$this->request->getAttribute('user_id', 0) - 不能用
isset($this->request->user_id)判断,得用array_key_exists('user_id', $this->request->getAttributes()) - 如果中间件没调用
withAttribute(),这里一定拿不到值——不是控制器问题,是中间件漏了
中间件顺序错会导致 getAttribute() 返回 null
TP 的中间件执行是链式调用,attribute 是附在当前 request 实例上的,如果 A 中间件设置了 user_id,但 B 中间件在 A 之后、控制器之前又调用了 $request->withAttribute() 却没透传原 attribute,就会丢失。
- 典型踩坑:自定义鉴权中间件里写了
$request = $request->withAttribute(...),但后面日志中间件做了$request = $request->withAttribute('log_id', ...)却没保留之前的,导致控制器收不到user_id - 解决办法:所有中间件都应基于上游传入的
$request调用withAttribute(),不要新建空 request - 调试技巧:在控制器开头打日志
var_dump($this->request->getAttributes()),确认 key 是否存在 - TP 自带的
SessionInit、TokenCheck等中间件都遵循该规范,放心组合
withAttribute() 的 key 名对大小写和下划线敏感
这个看似简单,却是最常被忽略的细节:PHP 数组 key 区分大小写,而 getAttribute() 是严格匹配字符串 key 的。
- 中间件设的是
$request->withAttribute('UserId', 123),控制器却写$this->request->getAttribute('user_id')→ 拿不到 - 建议统一用小写字母 + 下划线,比如
'auth_user_id'、'tenant_code',避免驼峰引发协作混乱 - 如果 key 来自外部(如 header 或 token payload),先
strtolower()或明确约定格式,别依赖原始 casing - TP 不做 key 归一化,也不会报错,静默失败——所以看到值为
null时,第一反应该查 key 字符串是否完全一致
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










