ThinkPHP 6 应在 app/middleware.php 中注册自定义全局参数过滤中间件(如 GlobalParamFilter),于 handle 方法中调用 $request->param() 后递归处理,优先过滤 JSON 请求体与嵌套数组,避免依赖已弃用的 filter_var 或不可靠的 request.filter 配置。

ThinkPHP 6 的 app_middleware 是最稳妥的全局过滤入口
所有接口请求都经过中间件,这里做参数过滤既统一又可控。别在每个控制器里重复写 htmlspecialchars 或 strip_tags,容易漏、难维护。
在 app/middleware.php 中注册中间件,例如:
return [
\think\middleware\CheckRequestCache::class,
\app\middleware\GlobalParamFilter::class, // 自定义中间件
];
注意:中间件顺序很重要,GlobalParamFilter 应放在路由解析之后、控制器执行之前(即不要放在最前,否则 $request->param() 还没组装好)。
- 过滤时机选
handle方法中调用$request->param()后处理,而非直接操作$_GET/$_POST—— ThinkPHP 已封装了参数合并逻辑,绕过它会导致input('name')行为不一致 - 不要用
filter_var($val, FILTER_SANITIZE_STRING)—— PHP 8.1+ 已弃用,且对多字节字符(如中文)处理不可靠 - 若需保留部分 HTML 标签(如富文本),过滤逻辑必须白名单控制,不能简单
strip_tags后再拼接
Request::filter 配置只影响 input(),不等于全局过滤
ThinkPHP 提供 request.filter 配置项,但它仅作用于 input()、param() 等方法返回值,对直接访问 $_POST、$request->post() 或 JSON body 解析结果无效。
立即学习“PHP免费学习笔记(深入)”;
配置示例(config/request.php):
'filter' => 'htmlspecialchars',
这看起来像“全局”,但实际有明显局限:
使用 JSON Schema 验证 JSON 数据,从示例 JSON 生成 schema,并将其转换为 TypeScript 接口、Python 数据类或 Markdown 文档。
- 对
input('content', '', 'html_entity_decode')这类显式指定过滤函数的调用,配置会被覆盖 - JSON 接口(
Content-Type: application/json)默认走$request->body()+json_decode,不经过filter链路 - 上传文件字段(
file类型)不会被该配置处理,需单独校验
JSON 请求体必须手动解码并过滤嵌套数组
ThinkPHP 默认不自动过滤 JSON 数据,$request->param() 对 JSON 请求只做一层解码,深层嵌套字段(如 {"user":{"name":"<script>..."}}</script>)里的字符串不会被 request.filter 触达。
正确做法是在中间件中判断请求类型,主动解码并递归过滤:
if ($request->isPost() && $request->header('content-type') === 'application/json') {
$data = json_decode($request->body(), true);
$filtered = $this->arrayHtmlSpecialChars($data);
// 替换原始参数(关键!)
$request->merge($filtered);
}
其中 arrayHtmlSpecialChars 需自己实现,注意跳过非字符串类型(如数字、布尔、null),避免破坏数据结构。
- 别用
json_encode(htmlspecialchars(...))—— 会把整个 JSON 字符串当文本转义,破坏结构 - 对
date、datetime字段,过滤前先验证格式,防止2024-01-01<script>这类混合输入绕过 - 如果用了
validate,过滤和验证顺序要明确:先过滤再验证,否则验证规则(如email)可能因特殊字符提前失败
第三方扩展包(如 topthink/think-filter)兼容性风险高
社区有封装过滤逻辑的扩展包,但 ThinkPHP 6.1+ 的请求生命周期变更后,很多包未更新,容易导致:
- 对
Route::rule()定义的变量(如/:id)过滤失效 - 与
Validate的scene模式冲突,部分字段被重复过滤 - 在命令行(
php think run)或队列任务中意外触发,造成非 HTTP 场景下参数异常
真实项目中,更推荐手写轻量中间件,控制粒度清晰。比如只对 POST/PUT/PATCH 方法启用过滤,GET 查询参数按需白名单放行(如搜索关键词允许空格、短横线)。
复杂点在于多层嵌套 + 多种编码混合(URL 编码、HTML 实体、JSON Unicode 转义),一个 htmlspecialchars 解决不了全部问题;容易被忽略的是文件上传后的临时路径名、回调 URL 中的 fragment 部分,这些地方同样需要独立校验。


















