前端跨域(二):JSONP

上一篇文章 前端跨域(一):CORS 實現了跨域的一種解決方案,IE8 和其餘瀏覽器分別經過 XDomainRequest 和 XHR 對象原生支持 CORS。此次我將補一補 Web 服務中也很是流行的一種跨域技術——JSONP,同時,將複用上次的前端跨域場景。javascript

1. JSONP(JavaScript Object Notation with padding,填充式JSON/參數式JSON)

【簡單理解】: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

2. 本地模擬 JSONP 跨域

接下來讓咱們來一步一步實現 JSONP,從中咱們也將看到,JSONP 跟 AJAX 毫無關係。git

(1)首先,若是不使用 JSONP,實現一個正常的函數調用:建立 2 個 <script> 標籤,一個用來定義函數以便處理數據,另外一個則用來進行函數調用。github

經過進行函數調用

(2)接着,咱們將第二個 <script> 標籤中的函數調用放到單獨的 js 文件中,更改一下傳入參數,進行驗證。web

經過引入 js 文件進行函數調用

(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中惟一須要先後端一塊兒約定字段的地方

經過在請求URL中加上查詢參數實現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 並非一回事兒。

3. jQuery 實現 JSONP

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);

jQuery實現JSONP

jsonp 可省略,或設置爲 false,則查詢參數就不會出如今 URL 中了,可是回調函數的名稱須要先後端約定,由於沒法從請求中獲取回調函數的名稱,後端只能將名稱寫死。

jsonpCallback 也可省略,jQuery 會自動生成一個隨機字符串做爲函數名,能夠減小沒必要要的命名工做。

4. 參考

5分鐘完全明白 JSONP
Understanding JSONP

相關文章
相關標籤/搜索