1窗看、什么是跨域請(qǐng)求
瀏覽器均默認(rèn)開啟了同源策略茸歧,它指Ajax請(qǐng)求所在的頁面和被請(qǐng)求的頁面在域名、端口均相同才能被訪問显沈,否則會(huì)提示如下錯(cuò)誤:
XMLHttpRequest cannot load xxxxxxx is not allowed by
Access-Control-Allow-Origin.
2举娩、JSONP解決方案
2.1 JSONP原理
JSONP 不是真正的AJAX請(qǐng)求,是利用script的src可可以跨域的特性构罗,動(dòng)態(tài)加載一段script腳本,腳本中包含需要的信息智玻。
2.2 基礎(chǔ)代碼實(shí)現(xiàn)
html源代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jsonp跨域</title>
<script type="text/javascript">
function callbak(rs){
alert(rs.data);
}
</script>
<script src="http://127.0.0.1:8080"></script>
</head>
<body>
</body>
</html>
node js 服務(wù)器代碼:
//調(diào)用http模塊
var http = require('http');
var server = http.createServer(function (request, response) {
response.writeHead(200, {
'Content-Type': 'application/javascript'
});
response.write("callbak({'data':'jsonp'});");
response.end();
});
server.listen(8080);
//打印日志
console.log('Http server is started. http://127.0.0.1:8080');
注意:
(1)函數(shù)名callbak 前端與后端需要一致
(2)服務(wù)器反饋的數(shù)據(jù)類型application/javascript 類型
2.3 JSONP 公用函數(shù)封裝遂唧。
前端代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>jsonp跨域</title>
</head>
<body id="body">
</body>
<!-- 主要要在body之后,否則document.body為null-->
<script type="text/javascript">
function jsonp(url,callback){
//動(dòng)態(tài)生成函數(shù)名稱
var funcName= "callback"+new Date().getTime();
//添加callback參數(shù)到url中(假設(shè)url中沒有其他參數(shù))
url = url + "?callback="+funcName;
//設(shè)置全局函數(shù)是傳人行數(shù)
window[funcName] = callback;
//創(chuàng)建Script標(biāo)簽
var script = document.createElement("script");
script.src = url;
//添加到body中
var body = document.body;
body.appendChild(script);
//動(dòng)態(tài)標(biāo)簽加載完成
script.onload = function(){
//刪除script標(biāo)簽
body.removeChild(script);
//刪除全局函數(shù)
window[funcName] = null;
}
}
var url = "http://127.0.0.1:8080";
jsonp(url,function(rs){
alert(rs.data);
});
</script>
</html>
node js 后端代碼:
//調(diào)用http模塊
var http = require('http');
var url = require('url');
var server = http.createServer(function (request, response) {
response.writeHead(200, {
'Content-Type': 'application/javascript'
});
//請(qǐng)求參數(shù)轉(zhuǎn)換為json格式
var arg = url.parse(request.url, true).query;
//獲取回調(diào)方法名稱
var funcName = arg.callback;
response.write(funcName + "({'data':'jsonp'});");
response.end();
});
server.listen(8080);
//打印日志
console.log('Http server is started. http://127.0.0.1:8080');
3吊奢、CROS解決方案
3.1 CROS簡(jiǎn)單
CORS需要瀏覽器和服務(wù)器同時(shí)支持盖彭。只有支持XmlHttpRequest Level2的瀏覽器才支持纹烹。
??整個(gè)CORS通信過程,都是瀏覽器自動(dòng)完成召边,不需要用戶參與铺呵。對(duì)于開發(fā)者來說,CORS通信與同源的AJAX通信沒有差別隧熙,代碼完全一樣片挂。瀏覽器一旦發(fā)現(xiàn)AJAX請(qǐng)求跨源,就會(huì)自動(dòng)添加一些附加的頭信息贞盯,有時(shí)還會(huì)多出一次附加的請(qǐng)求音念,但用戶不會(huì)有感覺。
??因此躏敢,實(shí)現(xiàn)CORS通信的關(guān)鍵是服務(wù)器闷愤。只要服務(wù)器實(shí)現(xiàn)了CORS接口,就可以跨源通信件余。
3.2 CROS服務(wù)器端設(shè)置
(1)Access-Control-Allow-Origin
??該字段是必須的讥脐。它的值要么是請(qǐng)求時(shí)Origin字段的值,要么是一個(gè)*啼器,表示接受任意域名的請(qǐng)求旬渠。
(2)Access-Control-Request-Method
??該字段是必須的,列出瀏覽器的CORS請(qǐng)求會(huì)用到哪些HTTP方法镀首。
**(3)Access-Control-Expose-Headers **
??該字段可選坟漱。CORS請(qǐng)求時(shí),XMLHttpRequest對(duì)象的getResponseHeader()方法只能拿到6個(gè)基本字段:Cache-Control更哄、Content-Language芋齿、Content-Type、Expires成翩、Last-Modified觅捆、Pragma。如果想拿到其他字段麻敌,就必須在Access-Control-Expose-Headers里面指定栅炒。
**(4)Access-Control-Allow-Credentials **
??該字段可選。它的值是一個(gè)布爾值术羔,表示是否允許發(fā)送Cookie赢赊。默認(rèn)情況下,Cookie不包括在CORS請(qǐng)求之中级历。設(shè)為true释移,即表示服務(wù)器明確許可,Cookie可以包含在請(qǐng)求中寥殖,一起發(fā)給服務(wù)器玩讳。這個(gè)值也只能設(shè)為true涩蜘,如果服務(wù)器不要瀏覽器發(fā)送Cookie,刪除該字段即可熏纯。
(5)Access-Control-Max-Age
??該字段可選同诫,用來指定本次預(yù)檢請(qǐng)求的有效期,單位為秒樟澜,在此期間误窖,不用發(fā)出另一條預(yù)檢請(qǐng)求。
3.3 CROS跨域Cookie
CORS請(qǐng)求默認(rèn)不發(fā)送Cookie和HTTP認(rèn)證信息往扔。如果要把Cookie發(fā)到服務(wù)器贩猎,一方面要服務(wù)器同意,指定Access-Control-Allow-Credentials字段,設(shè)置** Access-Control-Allow-Credentials: true**萍膛。
Access-Control-Allow-Credentials: true
另一方面吭服,開發(fā)者必須在AJAX請(qǐng)求中打開withCredentials屬性。
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
否則蝗罗,即使服務(wù)器同意發(fā)送Cookie艇棕,瀏覽器也不會(huì)發(fā)送〈埽或者沼琉,服務(wù)器要求設(shè)置Cookie,瀏覽器也不會(huì)處理桩匪。但是打瘪,如果省略withCredentials設(shè)置,有的瀏覽器還是會(huì)一起發(fā)送Cookie傻昙。這時(shí)闺骚,可以顯式關(guān)閉withCredentials,設(shè)置withCredentials=false;
特別注意
需要注意的是,如果要發(fā)送Cookie妆档,Access-Control-Allow-Origin就不能設(shè)為星號(hào)僻爽,必須指定明確的、與請(qǐng)求網(wǎng)頁一致的域名贾惦。同時(shí)胸梆,Cookie依然遵循同源政策,只有用服務(wù)器域名設(shè)置的Cookie才會(huì)上傳须板,其他域名的Cookie并不會(huì)上傳碰镜,且(跨源)原網(wǎng)頁代碼中的document.cookie也無法讀取服務(wù)器域名下的Cookie烙博。
3.4 CROS預(yù)檢
(1)預(yù)檢查概述
??非簡(jiǎn)單請(qǐng)求的CORS請(qǐng)求鞍泉,會(huì)在正式通信之前,增加一次HTTP查詢請(qǐng)求淌铐,稱為"預(yù)檢"請(qǐng)求(preflight)杰刽。瀏覽器先詢問服務(wù)器菠发,當(dāng)前網(wǎng)頁所在的域名是否在服務(wù)器的許可名單之中,以及可以使用哪些HTTP動(dòng)詞和頭信息字段贺嫂。只有得到肯定答復(fù)滓鸠,瀏覽器才會(huì)發(fā)出正式的XMLHttpRequest請(qǐng)求,否則就報(bào)錯(cuò)第喳。
(2)預(yù)檢查請(qǐng)求
??"預(yù)檢"請(qǐng)求用的請(qǐng)求方法是OPTIONS糜俗,表示這個(gè)請(qǐng)求是用來詢問的。頭信息里面曲饱,關(guān)鍵字段是Origin悠抹,表示請(qǐng)求來自哪個(gè)源。除了Origin字段扩淀,"預(yù)檢"請(qǐng)求的頭信息包括兩個(gè)特殊字段楔敌。Access-Control-Request-Method和 Access-Control-Request-Headers
(3)預(yù)檢查響應(yīng)
??服務(wù)器收到"預(yù)檢"請(qǐng)求以后,檢查了Origin驻谆、Access-Control-Request-Method和Access-Control-Request-Headers字段以后卵凑,確認(rèn)允許跨源請(qǐng)求,就可以做出回應(yīng)胜臊∩茁回應(yīng)的內(nèi)容參考服務(wù)端配置。
3.5 CROS 正常請(qǐng)求與回應(yīng)
一旦服務(wù)器通過了"預(yù)檢"請(qǐng)求象对,以后每次瀏覽器正常的CORS請(qǐng)求黑忱,就都跟簡(jiǎn)單請(qǐng)求一樣,會(huì)有一個(gè)Origin頭信息字段勒魔。服務(wù)器的回應(yīng)甫煞,也都會(huì)有一個(gè)Access-Control-Allow-Origin頭信息字段。