thinkphp 5 的 json() 方法默认不带 charset=utf-8,导致旧版 ie、微信 webview 等按 iso-8859-1 解析 utf-8 json 而乱码;default_charset 和 ini_set 均无效,须显式 header 或中间件统一注入。

ThinkPHP 5 默认的 json() 方法不带 charset=utf-8,浏览器(尤其是旧版 IE、微信 WebView)会按 ISO-8859-1 解析 UTF-8 编码的 JSON,导致中文显示为乱码或 符号——这不是数据错了,是响应头没声明编码。
为什么 json() 返回中文还是乱码
ThinkPHP 5.0–5.1 的 json() 方法只设置 Content-Type: application/json,不附带 charset=utf-8。而 PHP-FPM + Nginx 环境下,FastCGI 协议不传递 charset,全靠 PHP 主动写入响应头。一旦漏掉,前端就可能解析失败。
- Chrome 新版本有时“猜对”了,但不能依赖;iOS Safari、部分安卓 WebView 一定出问题
-
default_charset配置(如'default_charset' => 'utf-8')对json()完全无效,它只影响模板输出和输入解析 - 用
ini_set('default_charset', 'utf-8')同样无效,它只改htmlspecialchars()等函数的行为
控制器里最简修复:显式补 header
在单个接口需要快速修复时,直接在 return 前加 header() 是最快方式,且不会干扰框架流程。
使用 JSON Schema 验证 JSON 数据,从示例 JSON 生成 schema,并将其转换为 TypeScript 接口、Python 数据类或 Markdown 文档。
- 必须写在
return json($data)之前,且不能有任何前置输出(包括空格、BOM、echo) - 正确写法:
return json($data)->header('Content-Type', 'application/json; charset=utf-8'); - 错误写法:
header('Content-Type: application/json; charset=utf-8'); json($data);—— 没return,响应对象被丢弃,页面空白 - 如果用了
success()或error(),它们内部已带 charset,一般不用额外处理
全局生效:中间件统一注入 charset
比每个控制器加一行更可靠的方式,是在响应发出前统一检查并修正 Content-Type 头。
- 在
app/middleware.php中追加闭包(TP5.1+):if (strpos($response->getHeaderLine('Content-Type'), 'application/json') === 0) { $response = $response->withHeader('Content-Type', 'application/json; charset=utf-8'); } - 注意顺序:该逻辑需放在所有中间件链的末尾(即响应即将发出前),否则可能被后续中间件覆盖
- Apache 用户还要确认没有在
.htaccess或虚拟主机配置中强制覆写Content-Type,否则 PHP 层设置会被覆盖
调试时怎么确认是否生效
别只看浏览器控制台或前端报错,直接查原始响应头和内容最准。
- 打开 Chrome DevTools → Network → 点击对应请求 → Headers → Response Headers → 查看
Content-Type是否为application/json; charset=utf-8 - 切换到 Response 标签页,复制原始响应体,粘贴到 VS Code 中检查开头是否有不可见字符(BOM 或空格)
- 用
curl -I http://your-api.com/xxx查响应头,排除浏览器缓存干扰 - 如果用了 Nginx,检查是否配置了
fastcgi_hide_header Content-Type;或类似指令,这会导致 PHP 设置的头被隐藏
真正容易被忽略的不是“怎么加 charset”,而是 Web 服务器层(Nginx/Apache)是否在你不知情时悄悄覆盖了它——哪怕 PHP 代码写得再规范,只要服务器配置强制重写头,乱码就会重现。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










