实现 JSONP 跨域请求
手写 JSONP 函数实现跨域数据获取
问题
实现一个 JSONP 函数,用于跨域请求数据。
解答
基本实现
function jsonp(url, options = {}) {
return new Promise((resolve, reject) => {
const { params = {}, timeout = 5000, callbackKey = 'callback' } = options;
// 生成唯一的回调函数名
const callbackName = `jsonp_${Date.now()}_${Math.random().toString(36).slice(2)}`;
// 将回调函数挂载到全局
window[callbackName] = (data) => {
cleanup();
resolve(data);
};
// 清理函数
const cleanup = () => {
delete window[callbackName];
document.body.removeChild(script);
clearTimeout(timer);
};
// 超时处理
const timer = setTimeout(() => {
cleanup();
reject(new Error('JSONP request timeout'));
}, timeout);
// 构建请求 URL
const queryParams = { ...params, [callbackKey]: callbackName };
const queryString = Object.entries(queryParams)
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
.join('&');
const separator = url.includes('?') ? '&' : '?';
const requestUrl = `${url}${separator}${queryString}`;
// 创建 script 标签
const script = document.createElement('script');
script.src = requestUrl;
script.onerror = () => {
cleanup();
reject(new Error('JSONP request failed'));
};
document.body.appendChild(script);
});
}
使用示例
// 基本使用
jsonp('https://api.example.com/data', {
params: { id: 123 },
timeout: 3000
})
.then(data => console.log(data))
.catch(err => console.error(err));
// 服务端返回格式(以 Node.js 为例)
// jsonp_1699999999999_abc123({ "name": "test", "value": 100 })
服务端配合(Node.js 示例)
const http = require('http');
const url = require('url');
http.createServer((req, res) => {
const { query } = url.parse(req.url, true);
const callback = query.callback;
const data = { name: 'test', value: 100 };
// 返回函数调用形式
res.writeHead(200, { 'Content-Type': 'application/javascript' });
res.end(`${callback}(${JSON.stringify(data)})`);
}).listen(3000);
简化版本
function jsonpSimple(url, callbackName) {
return new Promise((resolve) => {
window[callbackName] = (data) => {
resolve(data);
delete window[callbackName];
document.body.removeChild(script);
};
const script = document.createElement('script');
script.src = `${url}?callback=${callbackName}`;
document.body.appendChild(script);
});
}
关键点
- 原理:利用
<script>标签不受同源策略限制的特性 - 只支持 GET:因为是通过 URL 加载脚本,无法发送 POST 请求
- 需要服务端配合:服务端返回
callback(data)格式的 JS 代码 - 安全风险:执行第三方返回的代码,存在 XSS 风险
- 已被淘汰:现代开发推荐使用 CORS 跨域
目录