跨域的解決方案(一):jsonp及其實(shí)現(xiàn)

作者博客: martin

最近遇到了跨域問(wèn)題,有很多種解決方案,本文主要對(duì)其中的jsonp進(jìn)行演示,本文使用node編寫(xiě)服務(wù)器端代碼進(jìn)行測(cè)試。

jsonp的來(lái)歷

ajax中的資源訪(fǎng)問(wèn)受到瀏覽器安全的'同源'限制,存在跨域問(wèn)題。
但是script標(biāo)簽的引入并不具有'同源'限制,不存在跨域問(wèn)題,利用這個(gè)特性進(jìn)行數(shù)據(jù)的獲取,就是jsonp。
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)形式)。
簡(jiǎn)單的一句話(huà):jsonp是一個(gè)協(xié)議,由于同源策略的限制,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)求。

jsonp的實(shí)現(xiàn)

從上文可知,jsonp的實(shí)現(xiàn)需要客戶(hù)端代碼和服務(wù)器端代碼共同努力。整體的思路如下圖:


Paste_Image.png

script標(biāo)簽不受跨域資源請(qǐng)求的影響,可以通過(guò)增加callback參數(shù)將回調(diào)函數(shù)傳遞,與服務(wù)器端配合從而在資源加載之后執(zhí)行該回調(diào)函數(shù),客戶(hù)端關(guān)鍵部分寫(xiě)法如下:

<!DOCTYPE html>
<body>
    <h1>測(cè)試jsonp</h1>
    <p>用node搭建能夠解析jsonp請(qǐng)求的服務(wù)器,前端通過(guò)script發(fā)送jsonp請(qǐng)求,通過(guò)回調(diào)處理服務(wù)器返回的數(shù)據(jù)</p>
    <script>
        function test(data){
            alert(data.name);
        };
    </script>
    <!-- 將test作為callback參數(shù)傳遞 -->
    <script src="http://localhost:3000/jsonp?callback=test"></script>
</body>

在服務(wù)器端,需要對(duì)script的src進(jìn)行url解析,將data作為參數(shù)放入回調(diào)函數(shù)中,最后通過(guò)res.end(callback(data))中將要執(zhí)行的函數(shù)放入客戶(hù)端的script中,在客戶(hù)端對(duì)該函數(shù)進(jìn)行執(zhí)行。
服務(wù)器端的關(guān)鍵代碼如下:

//解析url
var urlPath = url.parse(req.url).pathname;
console.log(urlPath);
//獲取從客戶(hù)端傳遞的參數(shù)
var qs = querystring.parse(req.url.split('?')[1]);
//約定的url的名稱(chēng)為jsonp
if(urlPath === '/jsonp' && qs.callback){
    res.writeHead(200,{'Content-Type':'application/json;charset=utf-8'});
    var data = {
        "name": "Monkey"
    };
    data = JSON.stringify(data);
    var callback = qs.callback+'('+data+');';
    //在end中返回callback(data),寫(xiě)入script中,進(jìn)而客戶(hù)端進(jìn)行該函數(shù)的執(zhí)行
    res.end(callback);
}

倉(cāng)庫(kù)在這里(倉(cāng)庫(kù)中的代碼相對(duì)復(fù)雜一點(diǎn),不過(guò)更加便于演示,具體看倉(cāng)庫(kù)中的說(shuō)明)這里要說(shuō)明的一點(diǎn)就是,url中"?callback=test"的形式其實(shí)是不必要的,只需要客戶(hù)端和服務(wù)器端協(xié)商一致即可。(這里我在寫(xiě)的時(shí)候,其實(shí)有一點(diǎn)疑惑:就是為什么把callback(data)傳遞回去就會(huì)在客戶(hù)端執(zhí)行?其實(shí)是因?yàn)榭蛻?hù)端是通過(guò)script請(qǐng)求的,獲取的內(nèi)容會(huì)放在<script></script>中,而res.end(data)中的data就會(huì)放到script中,瀏覽器會(huì)自動(dòng)調(diào)用其內(nèi)部代碼執(zhí)行,從而完成回調(diào)函數(shù)的執(zhí)行)。

總結(jié)

jsonp需要服務(wù)器端和客戶(hù)端配合使用,以script標(biāo)簽無(wú)'同源'限制的便利來(lái)實(shí)現(xiàn)跨域資源的共享。
優(yōu)點(diǎn):解決了跨域資源共享
缺點(diǎn):代碼麻煩,需要服務(wù)器端和客戶(hù)端約定好,同時(shí)客戶(hù)端傳遞數(shù)據(jù)只能通過(guò)url的形式,有長(zhǎng)度,安全等限制。
jsonp原理其實(shí)很簡(jiǎn)單,學(xué)習(xí)難度主要在于與服務(wù)器端的配合,這篇文章是我學(xué)習(xí)了node之后的一些想法,如有問(wèn)題,還請(qǐng)各位指點(diǎn)!

參考文章

  1. 借助node實(shí)戰(zhàn)JSONP跨域
  2. 前端跨域整理
  3. 瀏覽器同源政策及其規(guī)避方法
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容