jsonp 必须由控制器直接输出并终止响应,不能在视图中拼接;否则易混入空白、bom 或模板内容,导致前端解析失败、callback未定义或xss风险。

ThinkPHP 不该在视图里拼 JSONP,必须由控制器直接终止响应;否则极易混入空白、BOM 或模板输出,导致前端报 callback is not defined 或 Unexpected token。
为什么不能在视图中写 jsonp() 或手动拼字符串
视图(.html 或 .php 模板)本质是渲染层,它默认走 Layout、被模板引擎解析、可能被缓存,且无法控制 HTTP 头(如 Content-Type: application/javascript)。更关键的是:JSONP 要求响应体必须是严格合法的 JS 调用语句,而视图输出常带换行、空格、调试信息甚至 BOM 字节——这些都会让浏览器解析失败。
- 常见错误现象:
Uncaught ReferenceError: callback is not defined(实际是响应开头多了空格或 UTF-8 BOM) - 即使写了
<?php echo $_GET['callback'] ?>(<?php echo json_encode($data); ?>);,也绕不开模板引擎的自动转义和布局嵌套 - 视图不校验
callback参数合法性,直接拼接等于开放 XSS 入口
jsonp() 助手函数的正确用法与限制
jsonp() 是 ThinkPHP 5.1+ 内置的控制器级响应函数,它不经过视图,自动完成校验、设头、包裹、终止流程四件事。但它不是“加个回调参数的 json()”,而是完全独立的输出通道。
- 默认只读
$_GET['callback'],不支持 POST 或请求头传参 - 若
callback缺失、为空、含非法字符(如/、;、中文),函数直接返回空响应,不抛错也不提示 - 正则白名单为
/^[a-zA-Z_][a-zA-Z0-9_.]*$/,长度超 32 位也会拒绝 - ThinkPHP 6.0+ 默认禁用该功能,需在
app/middleware.php中显式注册think\middleware\Jsonp中间件 - 调用后立即
exit,后续代码(日志、跳转、return)全部失效
如何安全地手动构造 JSONP 响应
当需要统一回调名、兼容非 GET 请求、或配合 Nginx 缓存策略时,绕过 jsonp() 自己拼接更可控。核心就三步:设头、校验、输出、终止。
- 先用正则校验:
if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $callback)) { return json(['code' => 400, 'msg' => 'invalid callback'])->code(400); } - 设头:
header('Content-Type: application/javascript; charset=utf-8'); - 输出:
echo $callback . '(' . htmlspecialchars(json_encode($data), ENT_NOQUOTES) . ');'; - 必须以
exit或die结尾,否则框架可能追加 HTML 模板或中间件输出 - 注意:不要用
json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),因为斜杠不转义在 JS 字符串中可能破坏语法
Nginx 缓存下 JSONP 响应容易被污染
如果你的 ThinkPHP 站点前挂了 Nginx 且启用了 proxy_cache,默认缓存 key 不含 URL 参数,会导致不同用户的 callback=xxx 全部命中同一个缓存项——A 用户请求返回 B 用户的回调名,前端直接报错。
- 必须在 Nginx 配置中显式加入
$args:proxy_cache_key "$scheme$request_method$host$request_uri $args"; - 前端请求 URL 必须带
?callback=xxx,且xxx名称要符合后端白名单规则 - 如果接口约定用
cb而非callback,TP6 需配置'default_jsonp_handler' => 'cb'(在app.php中),TP5.1 则用jsonp($data, ['callback' => 'cb'])
最易被忽略的一点:JSONP 只能用于 GET 场景,且服务端无法可靠验证来源;只要前端能构造 script 标签,就能发起请求。所以任何涉及敏感操作(如删数据、改密码)的接口,绝不能依赖 JSONP 做权限控制。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











