一、 跨域請(qǐng)求的含義
2.3 使用 標(biāo)簽原生實(shí)現(xiàn) JSONP
經(jīng)過上面的事件,你是不是覺得 JSONP 的實(shí)現(xiàn)和 Ajax 大同小異?
其實(shí),由于實(shí)現(xiàn)的原理不同,由 JSONP 實(shí)現(xiàn)的跨域調(diào)用不是通過 XmlHttpRequset 對(duì)象,而是通過 script 標(biāo)簽,所以在實(shí)現(xiàn)原理上,JSONP 和 Ajax 已經(jīng)一點(diǎn)關(guān)系都沒有了。看上去形式相似只是由于 jQuery 對(duì) JSONP 做了封裝和轉(zhuǎn)換。
比如在上面的例子中,我們假設(shè)要傳輸?shù)臄?shù)據(jù) data 格式如下:
{
name:"chiaki",
id": "3001"
}
那么數(shù)據(jù)是如何傳輸?shù)哪??HTTP 請(qǐng)求頭的第一行如下:
GET /ajax/deal?callback=jsonpCallback&name=chiaki&id=3001&_=1473164876032 HTTP/1.1
可見,即使形式上是用 POST 傳輸一個(gè) JSON 格式的數(shù)據(jù),其實(shí)發(fā)送請(qǐng)求時(shí)還是轉(zhuǎn)換成 GET 請(qǐng)求。
其實(shí)如果理解 JSONP 的原理的話就不難理解為什么只能使用 GET 請(qǐng)求方法了。由于是通過 script 標(biāo)簽進(jìn)行請(qǐng)求,所以上述傳輸過程根本上是以下的形式:
這樣從服務(wù)器返回的代碼就可以直接在這個(gè) script 標(biāo)簽中運(yùn)行了。下面我們自己實(shí)現(xiàn)一個(gè) JSONP:
服務(wù)器 3000請(qǐng)求頁面的 JavaScript 代碼中,只有回調(diào)函數(shù) jsonpCallback:
functionjsonpCallback(data){
console.log("jsonpCallback: "+data.name)
}
服務(wù)器 3000請(qǐng)求頁面還包含一個(gè) script 標(biāo)簽:
服務(wù)器 3001上對(duì)應(yīng)的處理函數(shù):
app.get('/jsonServerResponse',function(req,res){
varcb=req.query.jsonp
console.log(cb)
vardata='var data = {'+'name: $("#name").val() + " - server 3001 jsonp process",'+'id: $("#id").val() + " - server 3001 jsonp process"'+'};'
vardebug='console.log(data);'
varcallback='$("#submit").click(function() {'+data+cb+'(data);'+debug+'});'
res.send(callback)
res.end()
})
與上面一樣,我們?cè)谒@取的參數(shù)后面加上 “ – server 3001 jsonp process” 代表服務(wù)器對(duì)數(shù)據(jù)的操作。從代碼中我么可以看到,處理函數(shù)除了根據(jù)參數(shù)做相應(yīng)的處理,更多的也是進(jìn)行字符串的拼接。
最終的結(jié)果為:
2.4 JSONP 總結(jié)
至此,我們了解了 JSONP 的原理以及實(shí)現(xiàn)方式,它幫我們實(shí)現(xiàn)前端跨域請(qǐng)求,但是在實(shí)踐的過程中,我們還是可以發(fā)現(xiàn)它的不足:
只能使用 GET 方法發(fā)起請(qǐng)求,這是由于 script 標(biāo)簽自身的限制決定的。
不能很好的發(fā)現(xiàn)錯(cuò)誤,并進(jìn)行處理。與 Ajax 對(duì)比,由于不是通過 XmlHttpRequest 進(jìn)行傳輸,所以不能注冊(cè) success、 error 等事件監(jiān)聽函數(shù)。
三、 使用 CORS 實(shí)現(xiàn)跨域調(diào)用
3.1 什么是 CORS?
Cross-Origin Resource Sharing(CORS)跨域資源共享是一份瀏覽器技術(shù)的規(guī)范,提供了 Web 服務(wù)從不同域傳來沙盒腳本的方法,以避開瀏覽器的同源策略,是 JSONP 模式的現(xiàn)代版。與 JSONP 不同,CORS 除了 GET 要求方法以外也支持其他的 HTTP 要求。用 CORS 可以讓網(wǎng)頁設(shè)計(jì)師用一般的 XMLHttpRequest,這種方式的錯(cuò)誤處理比 JSONP 要來的好。另一方面,JSONP 可以在不支持 CORS 的老舊瀏覽器上運(yùn)作?,F(xiàn)代的瀏覽器都支持 CORS。
3.2 CORS 的實(shí)現(xiàn)
還是以 服務(wù)器 3000 上的請(qǐng)求頁面向 服務(wù)器 3001 發(fā)送請(qǐng)求為例。
服務(wù)器 3000 上的請(qǐng)求頁面 JavaScript 不變,如下:
$(function(){
$("#submit").click(function(){
vardata={
name:$("#name").val(),
id:$("#id").val()
};
$.ajax({
type:'POST',
data:data,
url:'http://localhost:3001/cors',
dataType:'json',
cache:false,
timeout:5000,
success:function(data){
console.log(data)
},
error:function(jqXHR,textStatus,errorThrown){
console.log('error '+textStatus+' '+errorThrown);
}
});
});
});
服務(wù)器 3001上對(duì)應(yīng)的處理函數(shù):
app.post('/cors',function(req,res){
res.header("Access-Control-Allow-Origin","*");
res.header("Access-Control-Allow-Headers","X-Requested-With");
res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
res.header("X-Powered-By",' 3.2.1')
res.header("Content-Type","application/json;charset=utf-8");
vardata={
name:req.body.name+' - server 3001 cors process',
id:req.body.id+' - server 3001 cors process'
}
console.log(data)
res.send(data)
res.end()
})
在服務(wù)器中對(duì)返回信息的請(qǐng)求頭進(jìn)行了設(shè)置。
最終的結(jié)果為:
3.3 CORS 中屬性的分析
Access-Control-Allow-Origin
The origin parameter specifies a URI that may access the resource. The browser must enforce this. For requests without credentials, the server may specify “*” as a wildcard, thereby allowing any origin to access the resource.
Access-Control-Allow-Methods
Specifies the method or methods allowed when accessing the resource. This is used in response to a preflight request. The conditions under which a request is preflighted are discussed above.
Access-Control-Allow-Headers
Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request.
3.4 CORS 與 JSONP 的對(duì)比
CORS 除了 GET 方法外,也支持其它的 HTTP 請(qǐng)求方法如 POST、 PUT 等。
CORS 可以使用 XmlHttpRequest 進(jìn)行傳輸,所以它的錯(cuò)誤處理方式比 JSONP 好。
JSONP 可以在不支持 CORS 的老舊瀏覽器上運(yùn)作。
四、 一些其它的跨域調(diào)用方式
4.1 window.name
window對(duì)象有個(gè)name屬性,該屬性有個(gè)特征:即在一個(gè)窗口 (window) 的生命周期內(nèi),窗口載入的所有的頁面都是共享一個(gè) window.name 的,每個(gè)頁面對(duì) window.name 都有讀寫的權(quán)限,window.name 是持久存在一個(gè)窗口載入過的所有頁面中的,并不會(huì)因新頁面的載入而進(jìn)行重置。
4.2 window.postMessage()
這個(gè)方法是 HTML5 的一個(gè)新特性,可以用來向其他所有的 window 對(duì)象發(fā)送消息。需要注意的是我們必須要保證所有的腳本執(zhí)行完才發(fā)送 MessageEvent,如果在函數(shù)執(zhí)行的過程中調(diào)用了他,就會(huì)讓后面的函數(shù)超時(shí)無法執(zhí)行。
參考:https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage