jsonp() 是终止单次响应的方法,调用后立即退出、跳过视图、校验 callback 并设 header;它只读 GET 中的 callback 参数,严格限制函数名格式,且易受 Nginx 缓存污染导致前端报错。

Response::jsonp() 方法不是“加个回调名就完事”的简单封装,它会强制终止响应流程、覆盖 Content-Type、校验 callback 参数合法性,并跳过视图渲染——用错地方或忽略约束,前端必然报错。
为什么 jsonp() 一调用就停住,后面代码不执行
因为 jsonp() 内部直接调用了 exit 或 die,属于“终止单次响应”型方法。它不走正常返回流程,也不经过视图层。
- 常见错误现象:在
return jsonp($data)后还写日志、数据库操作、重定向,这些全被跳过 - 它和
json()的行为完全不同:json()是构造 Response 对象并返回,jsonp()是输出+退出 - ThinkPHP 6.0+ 默认禁用该功能,必须在
app/middleware.php中显式注册think\middleware\Jsonp中间件
jsonp() 只读 $_GET['callback'],且校验极严
它默认只从 GET 查询参数中提取 callback,不支持 POST、Header、自定义字段;且对函数名格式有硬性限制。
- 非法字符(如
/、;、空格)或长度超 32 字符 → 直接返回空响应,不报错也不提示 - 只允许以字母或下划线开头,后续仅支持字母、数字、下划线、点号、括号:
/^[a-zA-Z_][a-zA-Z0-9_.]*$/ - 若接口约定用
cb字段传回调名,必须显式指定:jsonp($data, ['callback' => 'cb']) - 全局统一改默认字段,需在
config/app.php中设'default_jsonp_handler' => 'cb'
Nginx 缓存下 JSONP 响应总串包
当 ThinkPHP 前面挂了 Nginx 且开了 proxy_cache,不同用户的 callback=xxx 请求极易被缓存到同一个 key 下,导致 A 用户收到 B 用户的回调名,前端直接报 Uncaught ReferenceError: xxx is not defined。
使用 JSON Schema 验证 JSON 数据,从示例 JSON 生成 schema,并将其转换为 TypeScript 接口、Python 数据类或 Markdown 文档。
立即学习“PHP免费学习笔记(深入)”;
- 这不是 PHP 层能解决的问题,是缓存策略缺陷
- 必须在 Nginx 配置中把请求参数纳入缓存 key:
proxy_cache_key "$scheme$request_method$host$request_uri $args"; - 漏掉末尾的
$args,所有 JSONP 请求就会共享缓存,无论 callback 值是什么 - 如果无法改 Nginx,只能退回到手动拼接 + 禁用缓存(如加
Cache-Control: no-cache)
手动构造 JSONP 比 jsonp() 更可控的场景
当你需要兼容非 GET 请求、统一回调名、配合业务状态码(如 400/401)、或嵌入自定义 header(如 Access-Control-Allow-Origin),绕过 jsonp() 自己拼更稳妥。
- 必须设 header:
Content-Type: application/javascript; charset=utf-8 - 必须校验
callback是否合法,否则 XSS 风险极高 - 必须用
htmlspecialchars(json_encode($data), ENT_NOQUOTES)防止 JSON 内引号破坏 JS 结构 - 必须以
exit或die结尾,否则框架可能追加 HTML 模板,导致语法错误 - 示例片段:
header('Content-Type: application/javascript; charset=utf-8');<br>echo $callback . '(' . htmlspecialchars(json_encode($data), ENT_NOQUOTES) . ');'<br>exit;
真正麻烦的从来不是怎么输出 callback({}),而是 callback 名是否被信任、响应是否被缓存污染、以及是否在预检(OPTIONS)阶段就被拦住——这些点一旦漏掉,前端永远收不到有效响应。


















