JSONP 教程(手把手讲解)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观
前言
在现代 Web 开发中,跨域资源共享(CORS)是一个绕不开的话题。当我们的前端应用需要从不同源获取数据时,浏览器的安全机制会严格限制这类请求。而 JSONP 教程 正是解决这一问题的经典方案之一。尽管 JSONP 已经被现代技术如 Fetch API 或 CORS 逐步替代,但它依然在特定场景下发挥着作用,并且是理解跨域原理的重要基础。本文将从零开始,以通俗易懂的方式讲解 JSONP 的原理、实现方法及实际案例,帮助开发者快速掌握这一技术。
JSONP 的基本原理
从同源策略说起
要理解 JSONP,首先需要了解浏览器的 同源策略(Same-Origin Policy)。同源策略要求请求的协议、域名、端口必须完全一致,否则浏览器会阻止请求。例如,如果网页地址是 http://a.com
,那么它无法直接访问 http://b.com/api
的数据。
JSONP 的核心思想是利用 <script>
标签的特殊性:浏览器允许 <script>
标签跨域加载外部 JavaScript 文件。因此,JSONP 通过动态创建 <script>
标签,向服务端发送请求并接收数据。
回调函数与数据包装
JSONP 的实现需要服务端和客户端的配合:
- 客户端:在请求中携带一个 回调函数名(如
callback=handleData
)。 - 服务端:将数据用客户端提供的函数名包装成可执行的 JavaScript 代码(如
handleData({data: 'Hello'})
)。 - 浏览器:将返回的代码执行,触发回调函数,从而获取数据。
比喻:
想象你住在小区 A,但快递员只能送到小区 B 的门口。这时你可以请快递员把包裹交给小区 B 的保安,并约定一个暗号(回调函数)。保安收到包裹后,会按暗号通知你来取,从而绕过直接进入小区 B 的限制。
JSONP 的实现步骤
前端实现:动态创建 <script>
标签
前端的核心逻辑是动态生成 <script>
标签,并定义回调函数。
示例代码
// 定义回调函数
function handleData(response) {
console.log('Received data:', response);
// 处理数据逻辑
}
// 动态创建 script 标签
const script = document.createElement('script');
script.src = 'https://api.example.com/data?callback=handleData';
document.body.appendChild(script);
关键点解释
callback=handleData
:请求参数中的callback
是服务端约定的键名,值为前端定义的函数名。script.src
:URL 中包含所有需要传递的参数,包括回调函数名。- 回调函数作用域:回调函数需要定义在全局作用域(如
window
对象)中,否则无法被调用。
后端实现:返回包装后的数据
服务端需要接收 callback
参数,并将数据包装成指定的函数调用格式。
示例(Node.js)
app.get('/data', (req, res) => {
const callback = req.query.callback;
const data = { message: 'Hello JSONP!' };
// 包装数据为 callback(data) 格式
const result = `${callback}(${JSON.stringify(data)})`;
res.set('Content-Type', 'application/javascript');
res.send(result);
});
后端注意事项
- 参数校验:严格限制
callback
参数的值,防止 XSS 攻击(如限制为字母和数字)。 - 内容类型:响应的
Content-Type
应为application/javascript
或text/javascript
。
JSONP 的实际案例
场景:调用第三方天气 API
假设我们要从 weather.example.com
获取天气数据,但该 API 仅支持 JSONP 格式。
前端代码
function showWeather(response) {
if (response.error) {
console.error('Failed to fetch weather:', response.error);
return;
}
document.getElementById('weather').innerHTML = `
Temperature: ${response.temperature}°C
<br>
Condition: ${response.condition}
`;
}
// 发起 JSONP 请求
const script = document.createElement('script');
script.src = 'https://weather.example.com/api?
callback=showWeather&city=Beijing';
document.body.appendChild(script);
后端响应示例
showWeather({
temperature: 25,
condition: 'Sunny',
error: null
});
常见问题与解决方案
- 回调函数未触发:检查函数是否在全局作用域,或服务端是否正确包装数据。
- 跨域错误:确保服务端支持 JSONP,并正确返回 JavaScript 格式。
- 数据格式错误:服务端返回的 JSON 可能缺少引号或逗号,导致语法错误。
JSONP 的优缺点分析
优点 | 缺点 |
---|---|
兼容性高:支持所有现代浏览器,甚至旧版 IE。 | 只支持 GET 请求,无法自定义 HTTP 头。 |
实现简单:无需复杂的配置,适合简单场景。 | 安全性较低:可能引发 XSS 攻击,需严格校验回调参数。 |
无跨域限制:绕过同源策略,适合与第三方 API 交互。 | 无法处理错误:如果服务端返回非 JSONP 格式,前端无法捕获异常。 |
JSONP 与现代技术的对比
与 CORS 的对比
CORS 是现代浏览器推荐的跨域解决方案,通过设置响应头 Access-Control-Allow-Origin
允许特定源访问资源。CORS 支持所有 HTTP 方法(GET/POST 等)和自定义头,安全性更高,但需要服务端配合。
与 Fetch API 的结合
在支持 Fetch API 的环境中,可以结合 CORS 实现更灵活的请求:
fetch('https://api.example.com/data', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ key: 'value' })
})
.then(response => response.json())
.then(data => console.log(data));
结论
JSONP 作为跨域通信的经典方案,至今仍在部分场景中发挥作用。通过动态 <script>
标签和回调函数,它巧妙地绕过了同源策略的限制。然而,开发者需注意其局限性,如仅支持 GET 请求和安全性风险。在现代开发中,建议优先使用 CORS 或 Fetch API,但在对接旧系统或第三方 API 时,JSONP 仍是一个值得掌握的工具。
掌握 JSONP 的核心逻辑,不仅能帮助开发者解决实际问题,还能更深入理解浏览器的安全机制和 HTTP 协议的底层原理。希望本文能成为你学习 JSONP 教程 的一份实用指南!