欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入分析jsonp協(xié)議原理

 更新時間:2015年09月26日 15:52:13   投稿:hebedich  
JSONP的最基本的原理是:動態(tài)添加一個<script>標(biāo)簽,而script標(biāo)簽的src屬性是沒有跨域的限制的。這樣說來,這種跨域方式其實(shí)與ajax XmlHttpRequest協(xié)議無關(guān)了。

今天在開發(fā)聯(lián)調(diào)的過程中,需要跨域的獲取數(shù)據(jù),因?yàn)槭褂玫膉query,當(dāng)然使用dataType:'jsonp'就能夠很easy的解決了。
但是因?yàn)楫?dāng)時后端沒有支持jsonp來訪問,后來他在實(shí)現(xiàn)這個功能的時候問了我一句,jsonp形式返回的格式是怎么樣子的?我一直以來只知道怎么使用,迷迷糊糊的卻沒有答上來。。。

雖然后來解決了,但是對于喜歡解決問題的我,心里卻一直耿耿于懷,必須得把這個研究透徹了,于是我開始翻閱資料,看到后面真有種豁然開朗的感覺,于是打算做個筆記與大家分享。

JSON和JSONP的區(qū)別

JSON和JSONP雖然只有一個字母的差別,但其實(shí)他們根本不是一回事兒:JSON是一種數(shù)據(jù)交換格式,而JSONP是一種跨域數(shù)據(jù)交互的協(xié)議,使用JSONP方法獲取到的仍然是json格式的數(shù)據(jù)。

說白了,用JSON來傳數(shù)據(jù),靠JSONP來跨域

JSONP詳細(xì)闡述

我們都知道,一個頁面的ajax只能獲取和此頁面同域的數(shù)據(jù)。,所以當(dāng)我們需要跨域獲取數(shù)據(jù)的時候就需要使用到JSONP方法來獲取了。

如下圖所示,就是使用json格式獲取跨域數(shù)據(jù)返回的錯誤提示:

那么該如何解決呢?使用框架的前端童鞋們可能都有自己相應(yīng)的辦法,比如jquery就是把dataType設(shè)為jsonp就能解決了,但是我們在使用的時候有沒有想過,為什么這樣就能解決呢?中心思想又是什么呢?

下面就開始為大家詳細(xì)闡述,首要思想就是利用scirpt標(biāo)簽來引入跨域的數(shù)據(jù)。我們從最開始慢慢來深入jsonp的過程。

引導(dǎo)步驟1

編寫b.com/b.js內(nèi)容:

復(fù)制代碼 代碼如下:
alert(‘hello');

然后編寫a.com/a.html內(nèi)容:

復(fù)制代碼 代碼如下:
<script type='text/javascript' src='http://b.com/b.js'>

運(yùn)行a.html,結(jié)果很明顯,肯定會彈出hello。

引導(dǎo)步驟2

修改b.com/b.js文件內(nèi)容:

復(fù)制代碼 代碼如下:
myFunction('hello');

然后修改a.com/a.html內(nèi)容:

<script type='text/javascript' src='http://b.com/b.js'>
<script>
function myFunction(str)
{ //定義處理數(shù)據(jù)的函數(shù)
alert(str + ' world');
}
</script>

運(yùn)行a.html 結(jié)果是彈出‘hello world'。這個應(yīng)該也毫無疑問。

引導(dǎo)步驟3

讓我們再看一下上面的步驟2,b.js中的‘hello'就是b.com域名下的數(shù)據(jù)了,而能夠在a.com/a.html中執(zhí)行顯示出來,這不就已經(jīng)實(shí)現(xiàn)了跨域請求數(shù)據(jù)了嗎?

另外,因?yàn)閟cript標(biāo)簽中的src 不一定要指向js文件,而可以指向任何地址。

所以,我們把上面步驟2中a.html的內(nèi)容:<script type='text/javascript' src='http://b.com/b.js'>,我們把其中的b.js改成b.html或者b.json等等都是可以的,執(zhí)行都能正常返回。

引導(dǎo)步驟4

上面的數(shù)據(jù)都是靜態(tài)的,是在文件內(nèi)寫死的,所以并不能滿足我們的需求了吧。。。因?yàn)槲覀僡jax請求數(shù)據(jù)是實(shí)時變化的,所以我們要把數(shù)據(jù)變成動態(tài)的了。

我們可以讓script表器去調(diào)用一個動態(tài)的頁面(接口),去實(shí)現(xiàn)獲取動態(tài)數(shù)據(jù),這里就想到了回調(diào)函數(shù).

編輯a.com/a.html頁面內(nèi)容:

<script type='text/javascript' src='http://b.com/b.aspx?callback=myFunction'>
 <script>
   function myFunction(str){ //定義處理數(shù)據(jù)的函數(shù)
    alert(str + ' world');
   }
</script>

我們在src引用地址中加了?callback=myFunction,意思是把顯示數(shù)據(jù)的函數(shù)也動態(tài)的傳入了。

使用jsonp方法獲取數(shù)據(jù),還有一個要點(diǎn)就是后端接口也要支持jsonp才行,比如下面一段代碼就是讓返回的數(shù)據(jù)變成jsonp的格式,請繼續(xù)看:(此處使用.net語言作為例子)

protected void page_load(object sender, EventArgs e){
  if(this.IsPostBack == false){
    string callback = '';
    if(Request["callback"] != null){
      callback = request["callback"];
      string data = "hello";
      Response.Write(callback+"("+ data + ")"); //接口頁面返回的數(shù)據(jù)格式“函數(shù)(參數(shù))”的格式。
    }
  }
}

代碼的意思很簡單,就是獲取調(diào)用函數(shù)的參數(shù)。如果這里調(diào)用b.aspx?callback=myFunction的話,則會返回myFunction('hello'),如果后端代碼給data賦值一個變量,這里的‘hello'則變成了動態(tài)的數(shù)據(jù)了。

引導(dǎo)步驟5

再看上面的步驟,雖然獲取的數(shù)據(jù)是動態(tài)的了,但在頁面上引入一個script標(biāo)簽,卻只能執(zhí)行一次,獲取一次,顯然還是不能滿足需求的。所以我們在需要的時候,就得動態(tài)的添加一次這樣的script標(biāo)簽。

所以我們在這里需要封裝一個函數(shù):

function addScript(src){
   var script = document.createElement('script');
   script.setAttribute('type','text/javascript');
   script.src= src;
   document.body.appendChild(script);
 }

需要調(diào)用的時候,就去執(zhí)行:

addScript('b.com/b.aspx?callback=myFunction');
 
   function myFunction(data){//定義處理數(shù)據(jù)的函數(shù)
     alert(data);
   }

ok,上面的過程就是jsonp的原理,我們不必去記住那些令人糾結(jié)不清的定義,只要看一遍這個過程,我相信就能明白其中的精髓了吧。

jquery實(shí)現(xiàn)跨域

jquery跨域方法

$.ajax({
  url: 'b.com/b.json', //不同的域
  type: 'GET', // jsonp模式只有GET是合法的
  dataType: 'jsonp', // 數(shù)據(jù)類型
  jsonp: 'callback', // 指定回調(diào)函數(shù)名,與服務(wù)器端接收的一致,并回傳回來
  success: function(data) {
    console.log(data);
  }
})

使用jquery非常方便,那么它是怎么實(shí)現(xiàn)這個轉(zhuǎn)化的呢?下面我們來看看這部分的jquery源碼。

jq實(shí)現(xiàn)jsonp源碼分析

我貼出網(wǎng)上給的jquery實(shí)現(xiàn)jsonp部分的源碼分析:

if (s.dataType == "jsonp") {   // 構(gòu)建jsonp請求字符集串。jsonp是跨域請求,要加上callback=?后面將會加函數(shù)名             
  if (type == "GET") { //使get的url包含 callback=?后面將 會進(jìn)行加函數(shù)名
    if (!s.url.match(jsre))      s.url += (s.url.match(/?/) ? "&" : "?") + (s.jsonp || "callback") + "=?";     
  } // 構(gòu)建新的s.data,使其包含 callback=function name
  else if (!s.data || !s.data.match(jsre))    s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
  s.dataType = "json";
}
//判斷是否為jsonp,如果是 ,進(jìn)行處理。
if (s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre))) {  
  jsonp = "jsonp" + jsc ++; //為請 求字符集串的callback=加上生成回調(diào)函數(shù)名
  if (s.data) s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");  
  s.url = s.url.replace(jsre, "=" + jsonp + "$1"); // 我們需要保證jsonp 類 型響應(yīng)能正確地執(zhí)行
     //jsonp的類型必須為script。這樣才能執(zhí)行服 務(wù)器返回的
     //代碼。這里就是調(diào)用這個回調(diào)函數(shù)。
  s.dataType = "script";
  //window下注冊一個jsonp回調(diào)函數(shù) 有,讓ajax請求返回的代碼調(diào)用執(zhí)行它,
  window[jsonp] = function(tmp) {   
    data = tmp;
    success();
    complete();   // 垃圾回收,釋放聯(lián)變量,刪除jsonp的對象,除去head中加的script元素  
    window[jsonp] = undefined;   
    try { 
      delete window[jsonp];     
    } catch (e) {}   
    if (head)  head.removeChild(script);   
  };  
}
if (s.data && type == "GET") {    // data有效,追加到get類型的url上去
  s.url += (s.url.match(/?/) ? "&" : "?") + s.data;    // 防止IE會重復(fù)發(fā)送get和post data
  s.data = null;
}
if (s.dataType == "script"  && type == "GET" && parts && (parts[1] && parts[1] != location.protocol || parts[2] != location.host)) {    // 在head中加上<script src=""></script>
  var head = document.getElementsByTagName("head")[0];   
  var script = document.createElement("script");   
  script.src = s.url;   
  if (s.scriptCharset) script.charset = s.scriptCharset;
  if (!jsonp) {  //如果datatype不是jsonp,但是url卻是跨域 的。采用scriptr的onload或onreadystatechange事件來觸發(fā)回 調(diào)函數(shù)。
    var done = false; // 對所有瀏覽器都加上處理器
    script.onload = script.onreadystatechange = function() {     
      if (!done && (!this.readyState || this.readyState == "loaded" || this.readyState == "complete")) {         
        done = true; 
        success();        
        complete();
        head.removeChild(script);       
      }   
    };  
  }  
  head.appendChild(script); // 已經(jīng)使用 了script 元素注射來處理所有的事情
  return undefined;
}

上面的代碼稍顯復(fù)雜,但是我們挑揀重要的看就好了。

我們來分析一下這個過程,其實(shí)這個過程也就是上面我提出問題的答案了:

這里執(zhí)行代碼之后,其實(shí)就是判斷是否配置了dataType: 'jsonp',如果是jsonp協(xié)議,則要在url上加callback=jQueryxxx(函數(shù)名),jquery會把url轉(zhuǎn)化為:http://b.com/b.json?callback=jQueryxxx,然后再在html中插入,加載完b.json這個文件后,就會執(zhí)行jQueryxxx這個回調(diào)函數(shù),而且此時這個函數(shù)里面已經(jīng)存在了動態(tài)數(shù)據(jù)(json格式數(shù)據(jù)),所以在頁面上執(zhí)行的時候就能夠隨心所欲的處理數(shù)據(jù)了,但是也別忘了后端也要支持jsonp格式才行。所以這樣就達(dá)到了跨域獲取數(shù)據(jù)的功能。

原生js封裝jsonp

function jsonp(config) {
    var options = config || {};  // 需要配置url, success, time, fail四個屬性
    var callbackName = ('jsonp_' + Math.random()).replace(".", "");
    var oHead = document.getElementsByTagName('head')[0];
    var oScript = document.createElement('script');
    oHead.appendChild(oScript);
    window[callbackName] = function(json) { //創(chuàng)建jsonp回調(diào)函數(shù)
      oHead.removeChild(oScript);
      clearTimeout(oScript.timer);
      window[callbackName] = null;
      options.success && options.success(json);  //先刪除script標(biāo)簽,實(shí)際上執(zhí)行的是success函數(shù)
    };
    oScript.src = options.url + '?' + callbackName;  //發(fā)送請求
    if (options.time) { //設(shè)置超時處理
      oScript.timer = setTimeout(function () {
        window[callbackName] = null;
        oHead.removeChild(oScript);
        options.fail && options.fail({ message: "超時" });
      }, options.time);
    }
  };

這是我自己寫的一個原生js實(shí)現(xiàn)jsonp獲取跨域數(shù)據(jù)的方法。

我們只需要調(diào)用jsonp函數(shù)就能夠跨域獲取數(shù)據(jù)了。比如:

jsonp({
    url: '/b.com/b.json',
    success: function(d){
      //數(shù)據(jù)處理
    },
    time: 5000,
    fail: function(){
      //錯誤處理
    }    
  })
小結(jié)

再說幾點(diǎn)注意的地方:

使用jsonp方法時,在控制臺的network-JS中才能找到調(diào)用的接口,不再是XHR類了。由于頁面渲染的時候script只執(zhí)行一次,而且動態(tài)數(shù)據(jù)需要多次調(diào)用,所以在插入使用之后需要刪除,并且要初始化回調(diào)函數(shù)。原生js實(shí)現(xiàn)時,最好加一個請求超時的功能,方便調(diào)試。

總之jsonp就是一種獲取跨域json數(shù)據(jù)的方法。

相關(guān)文章

  • 看了就知道什么是JSON

    看了就知道什么是JSON

    看了就知道什么是JSON...
    2007-12-12
  • 改進(jìn)版通過Json對象實(shí)現(xiàn)深復(fù)制的方法

    改進(jìn)版通過Json對象實(shí)現(xiàn)深復(fù)制的方法

    改進(jìn)版通過Json對象實(shí)現(xiàn)深復(fù)制的方法,需要的朋友可以參考下
    2012-10-10
  • json編寫簡單一例

    json編寫簡單一例

    主要是了解一下json的簡單寫法與應(yīng)用,方便想學(xué)習(xí)json的朋友
    2008-08-08
  • IE8 原生JSON支持

    IE8 原生JSON支持

    你可能已經(jīng)從這篇文章的標(biāo)題中猜到了,Internet Explorer 8(目前是Beta2)提供了原生JSON的解析和序列化。
    2009-04-04
  • JSON學(xué)習(xí)筆記

    JSON學(xué)習(xí)筆記

    JSON我就要來了
    2008-06-06
  • javascript json 新手入門文檔

    javascript json 新手入門文檔

    在異步應(yīng)用程序中發(fā)送和接收信息時,可以選擇以純文本和 XML 作為數(shù)據(jù)格式。掌握 Ajax 的這一期討論另一種有用的數(shù)據(jù)格式 JavaScript Object Notation(JSON),以及如何使用它更輕松地在應(yīng)用程序中移動數(shù)據(jù)和對象。
    2009-12-12
  • JSON.stringify 語法實(shí)例講解

    JSON.stringify 語法實(shí)例講解

    可能有些人對系列化這個詞過敏,我的理解很簡單。就是說把原來是對象的類型轉(zhuǎn)換成字符串類型(或者更確切的說是json類型的)。就這么簡單。打個比方說,你有一個類,那么你可以通過這個方法轉(zhuǎn)換成相應(yīng)的json類型的
    2012-03-03
  • json的前臺操作和后臺操作實(shí)現(xiàn)代碼

    json的前臺操作和后臺操作實(shí)現(xiàn)代碼

    通常情況下,json的在項(xiàng)目中的應(yīng)用都是在后臺把數(shù)據(jù)傳到前臺,然后前臺再獲取json中的數(shù)據(jù).
    2012-01-01
  • 一篇了解JSON與數(shù)據(jù)存儲基礎(chǔ)知識

    一篇了解JSON與數(shù)據(jù)存儲基礎(chǔ)知識

    這篇文章主要介紹了JSON與數(shù)據(jù)存儲基礎(chǔ)知識,本篇文章較為基礎(chǔ),但非常詳細(xì),可以很好的理解JSON和數(shù)據(jù)存儲,,需要的朋友可以參考下
    2023-01-01
  • 詳解Java中String JSONObject JSONArray List<實(shí)體類>轉(zhuǎn)換

    詳解Java中String JSONObject JSONArray List<實(shí)體類>轉(zhuǎn)換

    這篇文章主要介紹了詳解String JSONObject JSONArray List<實(shí)體類>轉(zhuǎn)換,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11

最新評論