JSONP一种解决跨域的古老方法

在修复博客欢迎卡片api接口的时候,想使用腾讯地图的接口,但是直接使用会出现跨域问题,搭建中转又太麻烦,在网上找到了一个很老的技术JSONP,完全符合我的需求。

JSONP(JSON with Padding)是一种非常经典、通用的前端跨域“黑客技巧”(Hack)。

被逼出来的方案:同源策略与漏洞

浏览器有一个铁律叫同源策略 (Same-Origin Policy):如果两个页面的域名、协议或端口不同,前端直接用 fetchAJAX 去请求对方的数据,浏览器就会报错拦截(也就是我们常说的 CORS 跨域报错)。

但在早期,前端工程师们发现了一个“官方漏洞”: 虽然 AJAX 被限制了,但是 HTML 里带有 src 属性的标签是不受跨域限制的

  • 你可以毫无阻碍地加载别人域名的图片:<img src="别人家的.jpg">
  • 也可以加载别人域名的 CSS:<link href="别人家的.css">
  • 最关键的是,你可以加载并执行别人域名的 JS 脚本:<script src="别人家的.js"></script>

JSONP 就是利用这个 <script> 标签可以跨域下载并执行代码的特性,巧妙地把数据“偷”回来的。

JSONP 是怎么运作的?

假设你的网站 a.com 想向 api.b.com 获取用户数据:

第一步:前端先在全局准备一个“接收器” 你先在自己的代码里定义好一个全局函数,等着接收数据。

window.receiveData = function(data) {
console.log("太棒了,拿到了跨域数据:", data);
};

第二步:前端动态创建 <script> 标签发请求 你用 JS 动态创建一个 script 标签,把目标接口写在 src 里,并且通过参数把刚才的函数名告诉后端

const script = document.createElement('script');
// 注意这里的 ?callback=receiveData
script.src = 'https://api.b.com/get_user?callback=receiveData';
document.body.appendChild(script); // 插入页面,浏览器立即发出请求

第三步:服务端配合“穿衣服”(最关键) api.b.com 的服务器收到请求,看到了 callback=receiveData 这个参数。 如果它只是返回纯粹的 JSON 数据 {"name": "张三"},浏览器的 script 标签是不认识的,会报错。 所以,服务端会把 JSON 数据当作参数,塞进你传过来的函数名里包裹起来(这就是 JSONP 中 Padding 的意思——填充/包裹),然后返回一段纯文本的JavaScript 代码:

receiveData({"name": "张三"});

第四步:浏览器自动执行代码 你的浏览器下载完这段文本后,把它当成正常的 JS 代码直接执行。这一执行,正好调用了你在第一步里定义好的 receiveData 函数,数据就被完美地传进了你的系统里!

为什么现在很少用 JSONP 了?

虽然 JSONP 巧妙地绕过了浏览器的安全封锁,但它有几个致命的硬伤,导致现在正规开发中已经被边缘化:

  • 只能发 GET 请求: 因为它本质上是给 <script> 标签赋值 src 来发起的,这就注定了它无法发送 POST、PUT、DELETE 等请求。如果你想上传一张图片或提交一大段表单,JSONP 毫无办法。
  • 巨大的安全隐患 (XSS 风险): JSONP 的本质是无条件信任并执行别人服务器返回的代码。如果那个 API 被黑客劫持,或者由于漏洞被注入了恶意代码,它返回的不再是数据,而是一句 alert('你被黑了') 甚至窃取你网站用户密码的逻辑,你的网站也会直接执行。
  • 错误处理极差: 使用现代的 fetch,你可以轻松拿到 404、500 等 HTTP 状态码并处理错误。但用 JSONP,如果请求失败了,你只能靠 script.onerror 知道“它没连上”,根本无法获取具体的错误原因。

总结: JSONP 是特定历史时期的产物,是一种靠前后端默契配合绕开规则的技巧。如今,标准的 CORS(跨域资源共享) 才是王道——也就是服务端在响应头里明确告诉浏览器 Access-Control-Allow-Origin: *,允许前端直接跨域请求。