上一篇文章 前端跨域(一):CORS 实现了跨域的一种解决方案,IE8 和其余浏览器分别经过 XDomainRequest 和 XHR 对象原生支持 CORS。此次我将补一补 Web 服务中也很是流行的一种跨域技术——JSONP,同时,将复用上次的前端跨域场景。javascript
【简单理解】:JSONP = 回调函数(Padding) + 数据(JSON),能够将 Padding 理解为回调函数,JSONP 则为被包含在函数调用中的 JSON。html
callback({ "name": "Nicholas" });
【原理】:Ajax 的跨域受到“同源策略”的限制,可是像 <script>
及 <img>
标签带有 src
属性,均可不受限制地其余域中加载资源,JSONP 则是经过动态 <script>
元素来使用的。前端
【实现方式】:回调函数是当响应到来时应该在页面中调用的函数,其名字通常在请求中指定。在请求完成后,即 JSONP 响应加载到页面中之后,就会当即执行。java
function handleResponse(response) { alert(response.city); } var script = document.createElement('script'); // 指定回调函数的名字为 handleResponse script.src = 'http://freegeoip.net/json/?callback=handleResponse'; document.body.insertBefore(script, document.body.firstChild);
【优势】:可以直接访问响应文本,支持在浏览器域服务器之间双向通讯。
【缺点】:JSONP 从其余域中加载代码执行,存在安全隐患,所以,使用时须要保障web服务安全可靠。jquery
接下来让咱们来一步一步实现 JSONP,从中咱们也将看到,JSONP 跟 AJAX 毫无关系。git
(1)首先,若是不使用 JSONP,实现一个正常的函数调用:建立 2 个 <script>
标签,一个用来定义函数以便处理数据,另外一个则用来进行函数调用。github
(2)接着,咱们将第二个 <script>
标签中的函数调用放到单独的 js
文件中,更改一下传入参数,进行验证。web
(3)到此为止, callback
函数是在本地的 js
文件中被调用的。如今,假设这个 js
文件在服务端,须要经过请求才能得到,则给 <script>
标签指定相应的 src
便可。ajax
Server 端:json
var http = require('http'); http.createServer(function(req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); // 发送字符串,等客户端得到响应时,即会调用该函数 res.end("callback('This is the callback from server.')"); }).listen(8888);
(4)上面的回调函数是在服务端写死的,而现实的状况,应该是客户端以参数的形式将回调函数名称传递给服务端,服务端获取这个变量,从而进行调用。这样,服务端就不用关心这个回调函数的名称是否改变了,并且前端也能够自行定义回调函数的名称。
OK,既然要传参,就得约定参数 key 值,这也是JSONP中惟一须要先后端一块儿约定字段的地方。
Server:
var http = require('http'); var url = require('url'); http.createServer(function(req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); // 解析 url 参数 var params = url.parse(req.url, true).query; // jsonpCallback 为先后端约定的字段,用于获取回调函数的名称 res.end(params.jsonpCallback + "('This is JSONP.')"); }).listen(8888);
(5)最后一步:为了提升代码的灵活性,实现 <script>
标签动态插入
<script> // 定义回调函数 var cb = function(data) { var oDiv = document.getElementById('content'); oDiv.innerHTML = data; } var url = 'http://localhost:8888?jsonpCallback=cb'; // 建立 script 标签,并设置其 src 属性 var script = document.createElement('script'); script.src = url; // 插入 body,此时调用开始 document.body.appendChild(script); </script>
瞧,整个过程,咱们并无用到 XHR 对象,只是利用了
<script>
的src
属性,所以 JSONP 与 AJAX 并非一回事儿。
jQuery 是前端常用的库,所以有必要了解 JSONP 在 jQuery 中的使用方式。
jQuery 将其包含在 $.ajax()
中,其中用到的具体有3个参数:
dataType(默认:none)
:预期服务器返回的数据类型('json', 'jsonp', 'xml', 'html', 'text')
jsonp(默认:“callback”)
: JSONP回调查询参数的名称,即先后端约定的字段名
jsonpCallback(默认:“jsonp{N}”)
:全局JSONP回调函数的字符串(或返回的一个函数)名。设置该项能启用浏览器的缓存。
Client:
var oDiv = document.getElementById('content'); // 定义回调函数 // 只是用于服务端获取名称,也能够自行实现,从而在 `success` 中进行调用 var cb = function() {}; $.ajax({ url: 'http://localhost:8888', type: 'get', dataType: 'jsonp', // 预期服务器返回的数据类型 jsonp: 'A_callback', // 指定回调查询参数的名称,即先后端约定的字段,默认为“callback" jsonpCallback: 'cb', // 指定回调函数名称 cache: true, success: function(data) { // jQuery 将 JSON 数据剥离出来,传入 success 和 error console.log(data); // 'This is JSONP realized by jQuery.' oDiv.innerHTML = data; } });
Server:不变
var http = require('http'); var url = require('url'); http.createServer(function(req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); // 解析 url 参数 var params = url.parse(req.url, true).query; // A_callback 为先后端约定的字段,用于获取回调函数的名称 res.end(params.A_callback+ "('This is JSONP realized by jQuery.')"); }).listen(8888);
jsonp
可省略,或设置为false
,则查询参数就不会出如今 URL 中了,可是回调函数的名称须要先后端约定,由于没法从请求中获取回调函数的名称,后端只能将名称写死。
jsonpCallback
也可省略,jQuery 会自动生成一个随机字符串做为函数名,能够减小没必要要的命名工做。