在现代 Web 应用程序的开发中,由于跨域访问的限制,经常需要在不同的域之间传输 JSON 数据。但是,浏览器强制遵循同源策略,限制了浏览器从一个域向另一个域请求数据的能力。为此,我们可以使用 JSONP(JSON with Padding)技术,它通过动态创建 script 标签实现了跨域传输 JSON 数据。
JSONP 实现了一个简单但非正式的协议,在客户端发出一个 GET 请求时,服务端返回一些 JavaScript 代码,使客户端能够利用这些代码执行回调。JSONP 非常适合用于构建跨域的 JSON 数据请求,但不能用于 POST 请求等其他类型的 Ajax 请求。本文将介绍如何在JavaScript中使用JSONP,并提供一个实际的例子。
实现JSONP
JSONP 可以通过以下代码实现:
function jsonp(url, callbackName, callback) { const script = document.createElement('script'); script.src = `${url}?callback=${callbackName}`; window[callbackName] = (data) => { callback(data); document.body.removeChild(script); }; document.body.appendChild(script); }
我们可以看到,这个函数有三个参数:
url
: JSONP 数据请求的 URL。callbackName
: 在服务端接受回调函数名的参数名。callback
: 接收 JSONP 数据的回调函数。
这个函数会动态地创建一个新的 script 标签,为获取 JSONP 数据请求 URL 添加一个查询参数 callback=callbackName
。回调函数的名字将保留在查询参数中,以便服务端能够读取。
然后,在全局命名空间中,我们暴露这个名为 callbackName
的回调函数,并在脚本标签的 load
事件处理函数上执行这个函数。核心代码如下:
window[callbackName] = (data) => { callback(data); document.body.removeChild(script); };
最后,我们再将新创建的 script 标签添加到文档中。因为该代码是通过 <script>
标签上定义的回调函数来执行的,所以这个 JSONP 请求的响应将被直接注入到我们的 JavaScript 中。
完整的代码如下:
function jsonp(url, callbackName, callback) { const script = document.createElement('script'); script.src = `${url}?callback=${callbackName}`; window[callbackName] = (data) => { callback(data); document.body.removeChild(script); }; document.body.appendChild(script); } jsonp('https://api.github.com/users/octocat', 'myCallback', (data) => { console.log(data); });
在这个例子中,我们向 GitHub API 发送了一个 JSONP 请求,当响应返回时,在控制台打印响应数据。此外,我们利用服务端接受 callback=myCallback
参数,来创建了用于响应数据的回调函数。
JSONP 的优缺点
JSONP 的优点:
- 浏览器支持良好,能在绝大多数环境下正常使用。
- 不需要 Ajax 请求,无需 XMLHttpRequest 对象,不需要进行跨域 AJAX 的额外工作。
JSONP 的缺点:
- 安全性问题。由于是在全局作用域上执行代码,所以恶意攻击者可以通过 JSONP 某些特定的接口来进行 CSRF(跨站请求伪造)。因此,我们需要在服务端进行一些针对性的安全策略。
- 只支持 GET 请求,不能支持 POST 等其他类型的 AJAX 请求。
- 只支持文本请求,不能传输二进制图像、JSON、XML 等类型的文件。
总结
JSONP 是一个使用非常广泛的跨域请求技术。通过简单的回调的方式,能方便地实现从不同域名的服务端动态地获得 JSON 数据。虽然使用起来比较方便,但也存在某些限制。JSONP 体现了一种传统 JavaScript 方式,如果不需要传输敏感数据或处理安全要求比较高的场景,我们仍然可以使用此技术。