(Ajax) 淺談 JSONP 的原理與實現

kimi013發表於2016-10-21

一、什麼是JSONP

1.1 同源策略

如果兩個頁面擁有相同的協議(如果指定),和主機,那麼這兩個頁面就屬於同一個
同源策略分為:

  • DOM同源策略:禁止對不同源頁面DOM進行操作

  • XMLHttpRequest同源策略:禁止向不同源的地址發起HTTP請求
    由此可見,Ajax禁止跨域。

1.2 JSONP的原理

JSONPJSON with Padding的簡稱,一般用來解決Ajax跨域的問題。它是這樣產生的:

  1. 頁面上呼叫js檔案時不受跨域的影響,而且,凡是擁有src屬性的標籤都擁有跨域的能力,比如<script><img><iframe>

  2. 可以在遠端伺服器上設法把資料裝進js格式的檔案裡,供客戶端呼叫處理,實現跨域。

  3. 目前最常用的資料交換方式是JSON,客戶端通過呼叫遠端伺服器上動態生成的js格式檔案(一般以JSON字尾)。

  4. 客戶端成功呼叫JSON檔案後,對其進行處理。

  5. 為了便於客戶端使用資料,逐漸形成了一種非正式傳輸協議,人們把它稱作JSONP,該協議的一個要點就是允許使用者傳遞一個callback引數給服務端,然後服務端返回資料時會將這個callback引數作為函式名來包裹住JSON資料,這樣客戶端就可以隨意定製自己的函式來自動處理返回資料了。

二、JSONP的客戶端具體實現

2.1 上文已指出,頁面可執行跨域的js程式碼(符合安全策略的),那麼:

<script type="text/javascript">
  var localHandler = function (data) {
    alert(`跨域的remote.js檔案可以呼叫本函式,帶來的資料是:` + data.result);
  };
</script>
<script type="text/javascript" src = "http://remoteserver.com/remote.js"></script>

remote.js的程式碼:

localHandler({
  "result": "我是遠端js帶來的資料"
});

執行後,成功彈出提示視窗,跨域成功。但問題是,如何讓遠端js知道它應該呼叫的本地函式叫什麼名字?

2.2 只要服務端提供的js指令碼是動態生成的就行了,呼叫者可以傳一個引數過去告訴服務端本地函式的名字,於是服務端就可以按照客戶的需求來生成js指令碼並相應了。

  var flightHandler = function (data) {
    alert(`你查詢的航班結果是:票價 ` + data.price + `元,餘票` + data.tickets + `張。`);
  };
  var url = `http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler`;
  var script = document.createElement(`script`);
  script.setAttribute(`src`, url);
  document.getElementsByTagName(`head`)[0].appendChild(script);

並沒有直接把遠端js寫死,而是編碼事項動態查詢。這是JSONP的核心部分。在呼叫的url中傳遞了一個code引數,告訴伺服器要查的是CA1998次航班的資訊,而callback引數告訴伺服器,本地呼叫的函式叫做flightHandler
伺服器的flightResult.aspx生成了以下程式碼提供給頁面:

flightHandler({
  "code": "CA1998",
  "price": 1780,
  "tickets": 5
});

執行一下頁面,成功提示視窗,JSONP的執行全過程順利完成。

2.3 jQuery程式碼

$(function() {
  $(`button`).on(`click`, function(event) {
    event.preventDefault();
    $.ajax({
      type: `GET`,
      async: false,
      url: `http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998`,
      dataType: `jsonp`,
      jsonp: `callback`,
      jsonpCallback: `flightHandler`,  // 預設為jQuery自動生成的隨機函式名
      success: function (json) {
        alert(`你查詢的航班結果是:票價 ` + json.price + `元,餘票` + json.tickets + `張。`);
      },
      error: function () {
        alert(`fail`);
      }
    });
  });
});

jQuery自動生成回撥函式並把資料取出來供success屬性方法來呼叫。

三、JSONPAjax的關係

  • AjaxJSONP這兩種技術看起來很像,目的也一樣,都是請求一個url,然後把伺服器返回的資料進行處理,因此jQuery等框架都把JSONP作為Ajax的一種形式。

  • 實際上AjaxJSONP有著本質上的不同。Ajax的核心是通過XMLHttpRequest獲取資料,而JSONP的核心則是動態新增<script>標籤來呼叫伺服器提供的js檔案。

  • AjaxJSONP的區別也不在於是否跨域,Ajax通過服務端代理也可以跨域,JSONP也可獲取同源資料。

四、參考

  1. 【原創】說說JSON和JSONP,也許你會豁然開朗,含jQuery用例

  2. 深入淺出JSONP–解決ajax跨域問題

  3. 瀏覽器的同源策略 – MDN

相關文章