JSONP(JSON with Padding)是一种跨域请求数据的技术,它利用 <script>
标签不受同源策略限制的特性,解决了浏览器中的跨域请求问题。通常,浏览器的同源策略(Same-Origin Policy)会限制从一个源加载的网页与不同源的服务器进行交互,JSONP 就是通过动态创建 <script>
标签,绕过这个限制,达到跨域请求数据的目的。
JSONP 的工作原理
JSONP 的工作原理可以分为以下几个步骤;
-
请求方式:JSONP 通过
<script>
标签发起跨域请求,因为<script>
标签不受同源策略的限制,可以从不同域加载资源。当客户端发送请求时,服务器返回的是一个 JavaScript 函数调用,而非普通的 JSON 数据。这样,客户端可以通过回调函数处理返回的数据,实现跨域访问。 -
如何发送请求:
- 客户端通过在页面中动态创建一个
<script>
标签,发起请求。例如:
const script = document.createElement('script'); script.src = 'https://moment.com/data?callback=myCallbackFunction'; document.body.appendChild(script);
- 在这个请求的 URL 中,callback 参数指定了一个回调函数的名称。这个回调函数的内容将会在服务器响应后执行。
- 客户端通过在页面中动态创建一个
-
服务端响应:
-
服务器端收到请求后,会返回一个包含回调函数调用的 JavaScript 代码。这段代码通常看起来像这样:
myCallbackFunction({ name: 'moment', age: 18, });
-
-
客户端处理响应:客户端在页面中定义了一个回调函数 myCallbackFunction,当服务器响应到达时,这个回调函数会被调用,并且包含服务器返回的数据:
function myCallbackFunction(data) { console.log(data); // { name: 'moment', age: 18 } }
JSONP 通过绕过浏览器的同源策略,解决了跨域问题,允许从不同域请求数据。其实现方式简单,客户端和服务器端只需做少量修改即可完成跨域请求。
它的缺点非常明显:
-
只支持 GET 请求:由于 JSONP 使用的是
<script>
标签,因此只支持 GET 请求,而不支持 POST 请求。这使得它不适用于所有的 API 请求,尤其是需要发送大量数据的请求。 -
安全性问题:JSONP 是通过执行返回的 JavaScript 代码来获取数据的,这可能会导致 XSS(跨站脚本攻击)等安全问题。如果返回的数据包含恶意脚本,可能会对客户端造成危害。
-
无错误处理机制:由于 JSONP 是通过动态脚本标签加载数据的,它没有内建的错误处理机制。一旦请求失败,可能无法很好地捕获错误。
可以通过使用 <iframe>
和 postMessage 进行跨域通信。这种方法适合于需要双向通信的场景,但是实现起来比 JSONP 要复杂一些。
代码示例
接下来我们编写一下客户端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>JSONP Example</h1>
<button onclick="requestData()">Get Data</button>
<p id="result"></p>
<script>
function requestData() {
const script = document.createElement('script');
script.src = 'http://localhost:3000/data?callback=handleResponse';
document.body.appendChild(script);
}
function handleResponse(data) {
document.getElementById('result').innerText = `Name: ${data.name}, Age: ${data.age}`;
}
</script>
</body>
</html>
首先,你需要在项目中安装 express。你可以使用以下命令安装它:
npm init -y
npm install express
然后再编写服务端代码:
const express = require('express');
const app = express();
const port = 3000;
app.get('/data', (req, res) => {
const data = {
name: 'moment',
age: 18,
};
const callback = req.query.callback;
res.send(`${callback}(${JSON.stringify(data)})`);
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
在上面的代码中,客户端通过点击按钮触发 requestData() 函数,这个函数会动态创建一个 <script>
标签,src 属性设置为服务器的 URL,并将 callback=handleResponse 作为查询参数传递给服务器。当服务器返回时,回调函数 handleResponse 会被调用,返回的数据作为参数传入
服务器接受请求,获取 callback 参数,并返回一个包装在 callback 函数中的数据。客户端收到响应后,会执行该回调函数并将数据传递给它。
总结
JSONP 是一种通过动态生成 <script>
标签绕过浏览器同源策略,实现跨域请求数据的技术。它的优势在于简单易用,但存在只支持 GET 请求、存在安全隐患等局限性。对于现代的应用,CORS 和其他技术(如 WebSocket)已经逐渐取代了 JSONP,成为更为安全和高效的跨域方案。