Flight框架默认不支持JSONP,需手动校验callback参数、拼接响应并设Content-Type为text/javascript;必须用$_GET['callback']且正则验证合法性,禁用Flight::json(),防范XSS与空响应。

Flight 框架默认不支持 JSONP,必须手动处理回调参数
Flight 的 json() 方法只输出纯 JSON,不会解析或包裹 callback 查询参数。直接返回 json($data) 给前端调用 JSONP 接口,浏览器会报语法错误——因为实际收到的是 {"a":1},而不是 cb({"a":1})。
正确做法是:手动读取 callback 参数,验证合法性,再拼接响应体。不要依赖任何“自动 JSONP 开关”——Flight 没这功能。
-
callback必须来自$_GET['callback'],不能从 POST 或 header 里取 - 值必须匹配正则
/^[a-zA-Z_][a-zA-Z0-9_]*$/,否则拒绝(防 XSS 注入) - 空值、点号、中括号、等号都算非法,例如
cb[a]、cb.a、cb=1全部拒掉 - 响应 Content-Type 必须设为
text/javascript,不是application/json
如何安全构造 JSONP 响应体(含字符转义)
PHP 的 json_encode() 默认不处理回调函数名里的特殊字符,但更关键的是:你不能把用户传来的 callback 原样拼进响应——哪怕它通过了正则校验,也要防止后续被用于构造恶意 JS 上下文(比如配合某些老浏览器的解析漏洞)。
安全做法是:仅允许字母、数字、下划线、美元符开头的标识符,并用 preg_replace 严格清洗;JSON 数据本身仍用 json_encode($data, JSON_UNESCAPED_UNICODE) 输出,避免双编码。
立即学习“PHP免费学习笔记(深入)”;
if (isset($_GET['callback']) && preg_match('/^[a-zA-Z_$][a-zA-Z0-9_$]*$/', $_GET['callback'])) {
$cb = $_GET['callback'];
$data = ['status' => 'ok', 'time' => time()];
header('Content-Type: text/javascript; charset=utf-8');
echo $cb . '(' . json_encode($data, JSON_UNESCAPED_UNICODE) . ');';
exit;
}
为什么不能用 header("Access-Control-Allow-Origin: *") 替代 JSONP
现代浏览器里,CORS 确实比 JSONP 更干净。但如果你的接口要被 IE8/9、旧版 Safari 或某些企业内网环境调用,它们不支持 XMLHttpRequest 的 CORS,只能靠 <script> 标签加载 JSONP。
这时候开 Access-Control-Allow-Origin: * 没用——脚本跨域请求不会带 Origin header,服务端根本收不到预检请求;而且即使开了,IE8/9 的 XDomainRequest 对象也不认这个 header。
- JSONP 是唯一兼容这些环境的方案
- CORS 和 JSONP 本质是两套机制,不能混用或互相替代
- 若同时支持两者,需在代码里区分请求方式:
$_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest'不代表一定是 CORS 请求,别误判
Flight 中 JSONP 接口容易忽略的两个细节
一是 Flight 的路由中间件(如 before)可能已输出过内容,导致 header 发送失败;二是 json_encode() 对资源类型(如 MySQL result resource)直接返回 null,而 JSONP 响应里出现 cb(null) 会让前端 callback 函数崩溃。
- 务必在
Flight::route()回调最开始就做callback校验和响应,避免被其他 hook 干扰 - 对数据源做兜底判断:
is_array($data) || is_object($data),非数组/对象一律转成空数组,不传null或resource - 不要在 JSONP 路由里调用
Flight::json(),它会强制设 header 并输出,和你手动写的冲突











