天天看點

JSONP跨域詳解

JSON和JSONP雖然隻有一個字母的差别,但其實他們根本不是一回事兒:JSON是一種資料交換格式,而JSONP是一種依靠開發人員的聰明才智創造出的一種非官方跨域資料互動協定。可見一個是描述資訊的格式,一個是資訊傳遞雙方約定的方法。

1、什麼是JSON?

2、什麼是JSONP?

2.1、先說下JSONP是怎麼産生的

1)一個衆所周知的問題,AJAX直接請求普通檔案存在跨域無權限通路的問題,甭管你是靜态頁面、動态網頁、web服務、WCF,隻要是跨域請求,一律不準;

2)不過我們又發現,Web頁面上調用js檔案時則不受是否跨域的影響(不僅如此,我們還發現凡是擁有src這個屬性的标簽都擁有跨域的能力,比如

<script>

<img>

<iframe>

);

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

2.2、JSONP的用戶端具體實作

1)我們知道,哪怕跨域js檔案中的代碼(當然指符合web腳本安全政策的),web頁面也是可以無條件執行的。

遠端伺服器

remoteserver.com

根目錄下有個

remote.js

檔案代碼如下:

alert('我是遠端檔案');
           

本地伺服器

localserver.com

下有個

jsonp.html

頁面代碼如下:

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script type="text/javascript" src="http://remoteserver.com/remote.js"></script>
  </head>
  <body></body>
</html>
           

2)現在我們在

jsonp.html

頁面定義一個函數,然後在遠端

remote.js

中傳入資料進行調用。

jsonp.html

頁面代碼如下:

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script type="text/javascript">
      var localHandler = function(data){
          alert('我是本地函數,可以被跨域的remote.js檔案調用,遠端js帶來的資料是:' + data.result);
      };
    </script>
    <script type="text/javascript" src="http://remoteserver.com/remote.js"></script>
  </head>
  <body></body>
</html>
           

remote.js

檔案代碼如下:

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

雖然跨域請求成功,當時怎麼讓遠端js知道它應該調用的本地函數叫什麼名字呢?

3)隻要服務端提供的js腳本是動态生成的,這樣調用這可以傳一個參數過去告訴服務端“我想要一段調用XXX函數的js代碼,請你傳回給我”,于是伺服器就可以按照用戶端的需求來生成js腳本并相應了。

jsonp.html

頁面的代碼:

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script type="text/javascript">
      // 得到航班資訊查詢結果後的回調函數
      var flightHandler = function(data){
          alert('你查詢的航班結果是:票價 ' + data.price + ' 元,' + '餘票 ' + data.tickets + ' 張。');
      };
      // 提供jsonp服務的url位址(不管是什麼類型的位址,最終生成的傳回值都是一段javascript代碼)
      var url = "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler";
      // 建立script标簽,設定其屬性
      var script = document.createElement('script');
      script.setAttribute('src', url);
      // 把script标簽加入head,此時調用開始
      document.getElementsByTagName('head')[0].appendChild(script); 
    </script>
  </head>
  <body></body>
</html>
           

這次沒有直接把遠端js檔案寫死,而是編碼實作動态查詢,這時JSONP用戶端實作的核心部分,本例中的重點也就在于如何完成JSONP調用的全過程。

我們看到調用的url中傳遞了一個

code

參數,告訴伺服器我要查的是CA1998次航班的資訊,而

callback

參數則告訴伺服器,我的本地回調函數叫做

flightHandler

,是以請把查詢結果傳入這個函數中進行調用。

這個叫做

flightResult.aspx

的頁面生成了一段這樣的代碼提供給

jsonp.html

(服務端的實作這裡就不示範了,與你選用的語言無關,說到底就是拼接字元串):

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

我們看到,傳遞給flightHandler函數的是一個json,它描述了航班的基本資訊。

2.3 總結原生JS實作JSONP的步驟

2.3.1 用戶端

定義擷取資料後調用的回調函數

動态生成對服務端JS進行引用的代碼

設定url為提供jsonp服務的url位址,并在該url中設定相關callback參數

建立script标簽,并設定其src屬性

把script标簽加入head,此時調用開始。

2.3.2 服務端

将用戶端發送的callback參數作為函數名來包裹住JSON資料,傳回資料至用戶端。

2.4 JSONP在jQuery中的具體實作

在jQuery中實作JSONP主要有兩種方式。

$.getJSON

$.ajax

2.4.1 $.getJSON實作方式

<script type="text/javascript" src="jquery.js"></script>  
<script type="text/javascript">  
    $.getJSON("http://crossdomain.com/services.php?callback=?", function(json){
        alert('您查詢到航班資訊:票價: ' + json.price + ' 元,餘票: ' + json.tickets + ' 張。');
    });  
</script>
           

2.4.2 $.ajax實作方式

<script type="text/javascript" src=jquery.min.js"></script>
<script type="text/javascript">
     $(document).ready(function(){ 
        $.ajax({
             type: "get",
             url: "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998",
             dataType: "jsonp",
             jsonp: "callback",//傳遞給請求處理程式或頁面的,用以獲得jsonp回調函數名的參數名(一般預設為:callback)
             jsonpCallback:"flightHandler",//自定義的jsonp回調函數名稱,預設為jQuery自動生成的随機函數名,也可以寫"?",jQuery會自動為你處理資料
             success: function(json){
                 alert('您查詢到航班資訊:票價: ' + json.price + ' 元,餘票: ' + json.tickets + ' 張。');
             },
             error: function(){
                 alert('fail');
             }
         });
     });
</script>
           

為什麼我這次沒有寫

flightHandler

這個函數呢?而且竟然也運作成功了!哈哈,這就是jQuery的功勞了,jquery在處理JSONP類型的ajax時(還是忍不住吐槽,雖然jQuery也把jsonp歸入了ajax,但其實它們真的不是一回事兒),自動幫你生成回調函數并把資料取出來供success屬性方法來調用。

3. JSONP與GET / POST 請求

我們知道script,link,img 等标簽引入外部資源,都是GET請求的,那麼就決定了 JSONP 一定是GET的。即便在$.ajax()中使用POST請求也能成功。一旦當我們指定

dataType: 'jsonp'

,不管指定type的值是什麼(GET/POST/甚至不寫),都會進行GET請求。

這是jQuery在封裝JSONP跨域時就已經寫死了的。

對應的源碼如下:

jQuery.ajaxPrefilter( "script", function( s ) {
    if ( s.cache === undefined ) {
        s.cache = false;
    }
    if ( s.crossDomain ) {
        s.type = "GET";
        s.global = false;
    }
});
           

if( s.crossDomain){ s.type = "GET"; ...}

這裡就是真相~ 在ajax的過濾函數中,隻要是跨域,jQuery就将其type設定成GET,真是那句話:在源碼面前,一切了無秘密~ jQuery源碼我自己很多地方讀不懂,但是并不妨礙我們去讀,去探索~

4. AJAX與JSONP的異同:

1)AJAX和JSONP這兩種技術在調用方式上“看起來”很像,目的也一樣,都是請求一個url,然後把伺服器傳回的資料進行處理,是以jQuery和extjs等架構都把JSONP作為AJAX的一種形式進行了封裝;

2)但AJAX和JSONP其實本質上是不同的東西。AJAX的核心是通過

XmlHttpRequest

擷取非本頁内容,而JSONP的核心則是動态添加

<script>

标簽來調用伺服器提供的js腳本。

3)是以說,其實AJAX與JSONP的差別不在于是否跨域,AJAX通過服務端代理一樣可以實作跨域,JSONP本身也不排斥同域的資料的擷取。

4)還有就是,JSONP是一種方式或者說非強制性協定,如同AJAX一樣,它也不一定非要用JSON格式來傳遞資料,如果你願意,字元串都行,隻不過這樣不利于用JSONP提供公開服務。

5)總而言之,JSONP不是AJAX的一個特例,哪怕jQuery等巨頭把它封裝進了AJAX,也不能改變這一點!

參考:公子七-JSONP跨域詳解