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 的实现需要服务端和客户端的配合:

  1. 客户端:在请求中携带一个 回调函数名(如 callback=handleData)。
  2. 服务端:将数据用客户端提供的函数名包装成可执行的 JavaScript 代码(如 handleData({data: 'Hello'}))。
  3. 浏览器:将返回的代码执行,触发回调函数,从而获取数据。

比喻
想象你住在小区 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/javascripttext/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  
});  

常见问题与解决方案

  1. 回调函数未触发:检查函数是否在全局作用域,或服务端是否正确包装数据。
  2. 跨域错误:确保服务端支持 JSONP,并正确返回 JavaScript 格式。
  3. 数据格式错误:服务端返回的 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 教程 的一份实用指南!

最新发布