AJAX 跨域請(qǐng)求 - JSONP獲取JSON數(shù)據(jù)

Asynchronous JavaScript and XML (Ajax) 是驅(qū)動(dòng)新一代 Web 站點(diǎn)(流行術(shù)語(yǔ)為 Web 2.0 站點(diǎn))的關(guān)鍵技術(shù)。Ajax 允許在不干擾 Web 應(yīng)用程序的顯示和行為的情況下在后臺(tái)進(jìn)行數(shù)據(jù)檢索。使用XMLHttpRequest函數(shù)獲取數(shù)據(jù),它是一種 API,允許客戶(hù)端 JavaScript 通過(guò) HTTP 連接到遠(yuǎn)程服務(wù)器。Ajax 也是許多 mashup 的驅(qū)動(dòng)力,它可將來(lái)自多個(gè)地方的內(nèi)容集成為單一 Web 應(yīng)用程序。

不過(guò),由于受到瀏覽器的限制,該方法不允許跨域通信。如果嘗試從不同的域請(qǐng)求數(shù)據(jù),會(huì)出現(xiàn)安全錯(cuò)誤。如果能控制數(shù)

據(jù)駐留的遠(yuǎn)程服務(wù)器并且每個(gè)請(qǐng)求都前往同一域,就可以避免這些安全錯(cuò)誤。但是,如果僅停留在自己的服務(wù)器上,Web

應(yīng)用程序還有什么用處呢?如果需要從多個(gè)第三方服務(wù)器收集數(shù)據(jù)時(shí),又該怎么辦?

理解同源策略限制

同源策略阻止從一個(gè)域上加載的腳本獲取或操作另一個(gè)域上的文檔屬性。也就是說(shuō),受到請(qǐng)求的 URL

的域必須與當(dāng)前 Web 頁(yè)面的域相同。這意味著瀏覽器隔離來(lái)自不同源的內(nèi)容,以防止它們之間的操作。這個(gè)瀏覽器策略很舊,從 Netscape

Navigator 2.0 版本開(kāi)始就存在。

克服該限制的一個(gè)相對(duì)簡(jiǎn)單的方法是讓 Web 頁(yè)面向它源自的 Web 服務(wù)器請(qǐng)求數(shù)據(jù),并且讓 Web 服務(wù)器像代理一樣將請(qǐng)求轉(zhuǎn)發(fā)給真正的第三方服務(wù)器。盡管該技術(shù)獲得了普遍使用,但它是不可伸縮的。另一種方式是使用框架要素在當(dāng)前 Web 頁(yè)面中創(chuàng)建新區(qū)域,并且使用GET請(qǐng)求獲取任何第三方資源。不過(guò),獲取資源后,框架中的內(nèi)容會(huì)受到同源策略的限制。

克服該限制更理想方法是在 Web 頁(yè)面中插入動(dòng)態(tài)腳本元素,該頁(yè)面源指向其他域中的服務(wù) URL

并且在自身腳本中獲取數(shù)據(jù)。腳本加載時(shí)它開(kāi)始執(zhí)行。該方法是可行的,因?yàn)橥床呗圆蛔柚箘?dòng)態(tài)腳本插入,并且將腳本看作是從提供 Web

頁(yè)面的域上加載的。但如果該腳本嘗試從另一個(gè)域上加載文檔,就不會(huì)成功。幸運(yùn)的是,通過(guò)添加 JavaScript Object Notation

(JSON) 可以改進(jìn)該技術(shù)。

1、什么是JSONP?

要了解JSONP,不得不提一下JSON,那么什么是JSON

JSON is a subset of the object literal notation of JavaScript. Since

JSON is a subset of JavaScript, it can be used in the language with no

muss or fuss.

JSONP(JSON with Padding)是一個(gè)非官方的協(xié)議,它允許在服務(wù)器端集成Script tags返回至客戶(hù)端,通過(guò)javascript callback的形式實(shí)現(xiàn)跨域訪(fǎng)問(wèn)(這僅僅是JSONP簡(jiǎn)單的實(shí)現(xiàn)形式)。

2、JSONP有什么用?

由于同源策略的限制,XmlHttpRequest只允許請(qǐng)求當(dāng)前源(域名、協(xié)議、端口)的資源,為了實(shí)現(xiàn)跨域請(qǐng)求,可以通過(guò)script標(biāo)簽實(shí)現(xiàn)跨域請(qǐng)求,然后在服務(wù)端輸出JSON數(shù)據(jù)并執(zhí)行回調(diào)函數(shù),從而解決了跨域的數(shù)據(jù)請(qǐng)求。

3、如何使用JSONP?

下邊這一DEMO實(shí)際上是JSONP的簡(jiǎn)單表現(xiàn)形式,在客戶(hù)端聲明回調(diào)函數(shù)之后,客戶(hù)端通過(guò)script標(biāo)簽向服務(wù)器跨域請(qǐng)求數(shù)據(jù),然后服務(wù)端返回相應(yīng)的數(shù)據(jù)并動(dòng)態(tài)執(zhí)行回調(diào)函數(shù)。

HTML代碼 (任一):

Html代碼

function?jsonpCallback(result)?{

//alert(result);

for(var?i?in?result)?{

alert(i+":"+result[i]);//循環(huán)輸出a:1,b:2,etc.

}

}

varJSONP=document.createElement("script");

JSONP.type="text/javascript";

JSONP.src="http://crossdomain.com/services.php?callback=jsonpCallback";

document.getElementsByTagName("head")[0].appendChild(JSONP);

或者

Html代碼

function?jsonpCallback(result)?{

alert(result.a);

alert(result.b);

alert(result.c);

for(var?i?in?result)?{

alert(i+":"+result[i]);//循環(huán)輸出a:1,b:2,etc.

}

}

JavaScript的鏈接,必須在function的下面。

服務(wù)端PHP代碼 (services.php):

Php代碼


//服務(wù)端返回JSON數(shù)據(jù)

$arr=array('a'=>1,'b'=>2,'c'=>3,'d'=>4,'e'=>5);

$result=json_encode($arr);

//echo?$_GET['callback'].'("Hello,World!")';

//echo?$_GET['callback']."($result)";

//動(dòng)態(tài)執(zhí)行回調(diào)函數(shù)

$callback=$_GET['callback'];

echo$callback."($result)";

如果將上述JS客戶(hù)端代碼用jQuery的方法來(lái)實(shí)現(xiàn),也非常簡(jiǎn)單。

$.getJSON

$.ajax

$.get

客戶(hù)端JS代碼在jQuery中的實(shí)現(xiàn)方式1:

Js代碼

$.getJSON("http://crossdomain.com/services.php?callback=?",

function(result)?{

for(variinresult)?{

alert(i+":"+result[i]);//循環(huán)輸出a:1,b:2,etc.

}

});

客戶(hù)端JS代碼在jQuery中的實(shí)現(xiàn)方式2:

Js代碼

$.ajax({

url:"http://crossdomain.com/services.php",

dataType:'jsonp',

data:'',

jsonp:'callback',

success:function(result)?{

for(variinresult)?{

alert(i+":"+result[i]);//循環(huán)輸出a:1,b:2,etc.

}

},

timeout:3000

});

客戶(hù)端JS代碼在jQuery中的實(shí)現(xiàn)方式3:

Js代碼

$.get('http://crossdomain.com/services.php?callback=?',?{name:?encodeURIComponent('tester')},function(json)?{for(variinjson)?alert(i+":"+json[i]);?},'jsonp');

其中 jsonCallback 是客戶(hù)端注冊(cè)的,獲取跨域服務(wù)器上的json數(shù)據(jù)后,回調(diào)的函數(shù)。

http://crossdomain.com/services.php?callback=jsonpCallback

這個(gè) url 是跨域服務(wù)器取 json 數(shù)據(jù)的接口,參數(shù)為回調(diào)函數(shù)的名字,返回的格式為

Js代碼

jsonpCallback({msg:'this?is?json?data'})

Jsonp原理:

首先在客戶(hù)端注冊(cè)一個(gè)callback, 然后把callback的名字傳給服務(wù)器。

此時(shí),服務(wù)器先生成 json 數(shù)據(jù)。

然后以 javascript 語(yǔ)法的方式,生成一個(gè)function , function 名字就是傳遞上來(lái)的參數(shù) jsonp.

最后將 json 數(shù)據(jù)直接以入?yún)⒌姆绞剑胖玫?function 中,這樣就生成了一段 js 語(yǔ)法的文檔,返回給客戶(hù)端。

客戶(hù)端瀏覽器,解析script標(biāo)簽,并執(zhí)行返回的 javascript 文檔,此時(shí)數(shù)據(jù)作為參數(shù),傳入到了客戶(hù)端預(yù)先定義好的 callback 函數(shù)里.(動(dòng)態(tài)執(zhí)行回調(diào)函數(shù))

使用JSON的優(yōu)點(diǎn)在于:

比XML輕了很多,沒(méi)有那么多冗余的東西。

JSON也是具有很好的可讀性的,但是通常返回的都是壓縮過(guò)后的。不像XML這樣的瀏覽器可以直接顯示,瀏覽器對(duì)于JSON的格式化的顯示就需要借助一些插件了。

在JavaScript中處理JSON很簡(jiǎn)單。

其他語(yǔ)言例如PHP對(duì)于JSON的支持也不錯(cuò)。

JSON也有一些劣勢(shì):

JSON在服務(wù)端語(yǔ)言的支持不像XML那么廣泛,不過(guò)JSON.org上提供很多語(yǔ)言的庫(kù)。

如果你使用eval()來(lái)解析的話(huà),會(huì)容易出現(xiàn)安全問(wèn)題。

盡管如此,JSON的優(yōu)點(diǎn)還是很明顯的。他是Ajax數(shù)據(jù)交互的很理想的數(shù)據(jù)格式。

主要提示:

JSONP 是構(gòu)建 mashup

的強(qiáng)大技術(shù),但不幸的是,它并不是所有跨域通信需求的萬(wàn)靈藥。它有一些缺陷,在提交開(kāi)發(fā)資源之前必須認(rèn)真考慮它們。

第一,也是最重要的一點(diǎn),沒(méi)有關(guān)于

JSONP 調(diào)用的錯(cuò)誤處理。如果動(dòng)態(tài)腳本插入有效,就執(zhí)行調(diào)用;如果無(wú)效,就靜默失敗。失敗是沒(méi)有任何提示的。例如,不能從服務(wù)器捕捉到 404

錯(cuò)誤,也不能取消或重新開(kāi)始請(qǐng)求。不過(guò),等待一段時(shí)間還沒(méi)有響應(yīng)的話(huà),就不用理它了。(未來(lái)的 jQuery 版本可能有終止 JSONP

請(qǐng)求的特性)。

JSONP 的另一個(gè)主要缺陷是被不信任的服務(wù)使用時(shí)會(huì)很危險(xiǎn)。因?yàn)?JSONP 服務(wù)返回打包在函數(shù)調(diào)用中的 JSON

響應(yīng),而函數(shù)調(diào)用是由瀏覽器執(zhí)行的,這使宿主 Web 應(yīng)用程序更容易受到各類(lèi)攻擊。如果打算使用 JSONP 服務(wù),了解它能造成的威脅非常重要。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容