必须通过$request->param()获取全部参数,再用$request->mergeParam($cleaned)注入清洗后数据,且中间件须注册在'http'数组中确保执行顺序;直接改$_GET/$_POST无效,$request->setParam()已废弃。

中间件里怎么拿到并修改原始请求参数
ThinkPHP 6 的 Request 对象默认不依赖 $_GET 或 $_POST,所以不能直接改超全局变量。必须通过 $request->param() 获取合并后的全部参数(含 GET、POST、路由变量),再用 $request->mergeParam($cleaned) 注入清洗后数据——这是唯一能影响后续所有 input() 和 param() 调用的方式。
常见错误是调用 $request->get() 和 $request->post() 后只处理其中一部分,漏掉 PUT/JSON 请求体;或用 $request->setParam()(TP6.0.x 已废弃),导致清洗无效。
-
$request->param()返回的是“最终可用参数”,包含所有来源,应作为清洗入口 - 对数组值要递归判断:
is_string($v)才执行htmlspecialchars(),跳过文件上传字段、整数、布尔值等 - 不要在中间件里调用
input(),会触发重复过滤链甚至无限递归
为什么不能直接遍历 $_GET / $_POST
ThinkPHP 6 从底层解耦了超全局变量,$_GET 和 $_POST 不再是参数唯一来源:JSON 请求体、PUT 表单、路由绑定参数都会被 param() 合并进来。直接操作 $_GET 只能清洗 URL 查询参数,漏掉 80% 的 POST/JSON 数据,且 PHP 8.1+ 会对直接赋值 $_GET 发出警告。
更危险的是,有些中间件注册在路由解析前(如 'route' 数组),此时 $request->param() 还没合并路由变量,拿到的只是空数组——必须确保中间件注册在 'http' 全局中间件数组里,且顺序靠前。
立即学习“PHP免费学习笔记(深入)”;
- TP6 默认中间件执行顺序:路由解析 → 中间件 → 控制器,
'http'数组里的中间件才在路由之后运行 -
filter_input_array()在 ThinkPHP 中不适用,它只读$_GET/$_POST,无法覆盖 JSON 或 PUT - 若项目启用了多入口(如 API 和 Web 分开),需确认中间件是否被两个入口同时加载
过滤时如何避开 JSON 和文件字段
对所有字符串字段无差别执行 htmlspecialchars() 会导致 JSON 字段变成乱码(如 {"name":"<script>"}</script> → {"name":"<script>"}),前端解析失败。富文本字段如果已用 HTMLPurifier 处理过,再套一层 htmlspecialchars() 会双重编码。
正确做法是按字段名白名单或类型做选择性清洗:只处理明确用于 HTML 输出的字段(如 title、content),跳过 data、payload、file 等标识字段,也跳过非字符串类型。
- 用
array_key_exists('content', $params)判断字段是否存在,避免isset($params['content'])对 null 值误判 - 检测字段是否为 JSON 字符串:
is_string($v) && is_object(json_decode($v, true)),则跳过 - 文件上传字段值是
think\File实例,is_object($v) && method_exists($v, 'getInfo')可识别
中间件注册后为什么 filter 没生效
最常踩的坑是中间件没 return $next($request),或者返回了非 Response 对象(比如忘了写 return,PHP 默认返回 null),导致请求链中断,页面空白但无报错。另一个隐蔽问题是框架缓存了 param() 结果,清洗后没刷新内部缓存。
$request->mergeParam($cleaned) 是强制刷新缓存的关键,不是可选操作。TP6.1+ 支持该方法,旧版 TP6 需用反射修改 $request->param 属性,但极易出错,不建议。
- 中间件 handle 方法末尾必须有
return $next($request);,缺 return 或写成$next($request);(无 return)都会中断流程 - 检查
app/middleware.php是否把中间件类加到了'http'键下,而不是'route'或'event' - 开发环境开启调试模式后,在控制器里 dump
$this->request->param(),对比中间件前后值,确认是否真被修改
mergeParam 替换参数」和「确保中间件在 http 链中执行」这两步上,其余逻辑哪怕写得再细,只要这两处错了,整个过滤就形同虚设。



















