ThinkPHP 6 默认开启 htmlspecialchars 全局过滤,导致 JSON、HTML 等原始内容被破坏;应禁用 default_filter 并按字段手动处理,JSON 请求需用 getContent() 解析。

ThinkPHP 6 默认开启自动转义,htmlspecialchars 会破坏表单原始内容
ThinkPHP 6 默认在 Request 对象初始化时对 $_POST 和 $_GET 做了全局 htmlspecialchars 过滤(通过 think\helper\Str::clean),导致提交含 &、、<code>>、" 的内容(如 JSON 字符串、HTML 片段、SQL 片段)被转义成 &、< 等,后端接收到的是“已损坏”的字符串。
这不是安全加固,而是过度过滤——尤其当你的接口本就约定接收 raw 数据时,这种预处理反而成了 bug 源头。
- 检查是否启用:查看
config/app.php中'default_filter' => 'htmlspecialchars'是否存在;或确认是否在中间件/全局事件中手动调用了Request::filter() - 临时绕过方法:用
$this->request->rawInput()获取原始 POST body(适用于 JSON 提交) - 更通用的解法:在控制器中对特定字段手动还原:
htmlspecialchars_decode($data['content'], ENT_QUOTES)
表单提交含 HTML 或富文本时,htmlspecialchars_decode 不够用
如果用户提交的是带样式标签的富文本(比如 <span style="color:red">警告</span>),单纯用 htmlspecialchars_decode 只能还原编码,但无法解决嵌套引号、属性值截断等问题。此时应区分场景:
- 若后端需原样存储并前端直接
v-html渲染:建议改用base64_encode(file_get_contents('php://input'))接收原始流(需前端配合设置Content-Type: text/plain) - 若只是想保留合法 HTML:改用
html_entity_decode($str, ENT_QUOTES | ENT_HTML5, 'UTF-8'),注意必须显式指定ENT_HTML5,否则 PHP 7.4+ 默认按 HTML4 解析,对'等支持不一致 - 避免在模型层或验证器里做 decode —— 容易遗漏,且违反单一职责;应在控制器入口统一处理
自定义表单字段过滤规则,禁用默认 htmlspecialchars 但保留其他安全机制
最干净的做法是关闭全局自动转义,改由你按字段控制。在 config/app.php 中注释或删除 'default_filter' 配置项后,TP6 不再自动处理输入,但你仍可通过以下方式补位:
立即学习“PHP免费学习笔记(深入)”;
- 对普通文本字段(如标题、摘要):在验证器中使用
rule => ['trim', 'max:100'],不加htmlspecialchars - 对可能含脚本的字段(如评论、描述):验证器里加
'safe' => true并配合think\facade\Security::stripscript()移除 script/style 标签 - 关键字段(如用户名、邮箱)仍建议走
filter_var($value, FILTER_SANITIZE_EMAIL)等标准过滤,而非依赖框架默认行为
AJAX 提交 JSON 时,Request::param() 返回空数组?
这是典型误区:当前端用 fetch 发送 Content-Type: application/json 请求时,TP6 的 Request::param() 默认只解析 application/x-www-form-urlencoded 和 multipart/form-data,对 JSON body 直接忽略,返回空数组。
正确做法是显式读取原始体并解码:
$json = $this->request->getContent();
$data = json_decode($json, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new ValidateException('JSON 格式错误');
}
注意不要混用 Request::post()(它只读 $_POST,而 JSON 不进 $_POST);也不要依赖 input('param_name') —— 它底层仍走 param()。
如果你的项目大量使用 JSON 提交,建议封装一个 jsonInput() 方法到基控制器,避免每个接口重复写 getContent + json_decode。



















