前端程序使用extjs寫(xiě),在本地測(cè)試,發(fā)送請(qǐng)求到服務(wù)器時(shí),發(fā)現(xiàn)存在跨域的問(wèn)題,cookie也沒(méi)有set成功,于是乎在這里整理一下解決過(guò)程
由于篇幅較長(zhǎng),不想看解決過(guò)程的可以翻到最后看總結(jié)
1.跨域允許
2.客戶(hù)端無(wú)法攜帶跨域cookie
3.因?yàn)榧恿藈ithCredentials報(bào)文頭,可是客戶(hù)端不知道服務(wù)器允不允許報(bào)的錯(cuò)
4.由于客戶(hù)端不知道服務(wù)端是否允許POST請(qǐng)求而報(bào)的錯(cuò)
假設(shè)我的服務(wù)器IP是120.111.111.123
# 本地的html
# index.html
<html>
<head>
<meta charset="utf8">
</head>
<body>
<input type="button" onclick="request()" value="請(qǐng)求">
</body>
<script type="text/javascript" src="./ext-all.js"></script>
<script type="text/javascript">
function request(){
Ext.Ajax.request({
url: 'http://120.111.111.123/setcookie.php',
method: 'POST',
params: {
'text': 'hello world'
},
success: function(transport){
// do something
},
failure: function(transport){
alert("Error: " - transport.responseText);
}
});
}
</script>
</html>
#服務(wù)器的php文件
#path setcookie.php
<?php
session_start();
?>
點(diǎn)擊“請(qǐng)求”按鈕,發(fā)送請(qǐng)求后發(fā)現(xiàn)js報(bào)錯(cuò)
XMLHttpRequest cannot load http://120.111.111.123/setcookie.php.
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'null' is therefore not allowed access.
報(bào)這個(gè)錯(cuò)就說(shuō)明我們跨域了,不在允許的訪問(wèn)源,于是乎我在服務(wù)的setcookie.php加入header('Access-Control-Allow-Origin:*');
允許所有源
<?php
session_start();
header('Access-Control-Allow-Origin:*');
// 功能...
// ...
然后又報(bào)錯(cuò)
XMLHttpRequest cannot load http://120.111.111.123/setcookie.php. Request header field X-Requested-With is not allowed by Access-Control-Allow-Headers in preflight response.
這次的報(bào)錯(cuò)是因?yàn)椋诳缬虻臅r(shí)候,extjs不會(huì)直接發(fā)post請(qǐng)求,而是先發(fā)送一個(gè)option請(qǐng)求,看看服務(wù)器允許什么訪問(wèn)頭(比如是不是允許post請(qǐng)求),驗(yàn)證成功后才會(huì)發(fā)送真正的請(qǐng)求
#用谷歌的開(kāi)發(fā)者工具抓的option報(bào)文
OPTIONS /setcookie.php HTTP/1.1
Host: 120.111.111.123
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Access-Control-Request-Method: POST
Origin: null
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
Access-Control-Request-Headers: x-requested-with
Accept: */*
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
接下來(lái),我們只要發(fā)送我們?cè)试S什么請(qǐng)求頭就行了
#path /setcookie.php
session_start();
header('Access-Control-Allow-Origin:*');
header('Access-Control-Allow-Methods:OPTIONS, GET, POST'); // 允許option,get,post請(qǐng)求
header('Access-Control-Allow-Headers:x-requested-with'); // 允許x-requested-with請(qǐng)求頭
header('Access-Control-Max-Age:86400'); // 允許訪問(wèn)的有效期
// 功能...
// ...
繼續(xù)測(cè)試我們的新功能,成功的解決了跨域問(wèn)題
but,cookie沒(méi)有“設(shè)置成功”。而之所以沒(méi)有“設(shè)置成功”,是因?yàn)閏ookie存在本地,但是每個(gè)cookie都有一個(gè)domain,當(dāng)你本地的cookie中存在你當(dāng)前訪問(wèn)的域時(shí),才會(huì)被帶過(guò)去,而我的index.html文件是本地訪問(wèn)的,即http://localhost/index.html,而cookie的域是120.111.111.123的,所以不行了。于是乎繼續(xù)改
#path index.html
<html>
<head>
<meta charset="utf8">
</head>
<body>
<input type="button" onclick="request()" value="請(qǐng)求">
</body>
<script type="text/javascript" src="./ext-all.js"></script>
<script type="text/javascript">
function request(){
Ext.Ajax.request({
url: 'http://120.111.111.123/setcookie.php',
method: 'POST',
params: {
'text': 'hello world'
},
withCredentials: true, # 加了這個(gè)
success: function(transport){
// do something
},
failure: function(transport){
alert("Error: " - transport.responseText);
}
});
}
</script>
</html>
繼續(xù)訪問(wèn),報(bào)錯(cuò)
XMLHttpRequest cannot load http://120.111.111.123/setcookie.php.
Response to preflight request doesn't pass access control check: A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header when the credentials flag is true.
Origin 'null' is therefore not allowed access.
The credentials mode of an XMLHttpRequest is controlled by the withCredentials attribute.
現(xiàn)在這個(gè)錯(cuò)誤產(chǎn)生的原因就是
1.因?yàn)榧尤肓藈ithCredentials之后,Access-Control-Allow-Origin就不能用“*”了,既然不允許訪問(wèn)這個(gè)源,那我就讓你發(fā)個(gè)報(bào)文頭讓你允許訪問(wèn)唄!
<?php
#path setcookie.php
session_start();
// 是否存在請(qǐng)求源
if(isset($_SERVER["HTTP_ORIGIN"])) {
header('Access-Control-Allow-Origin:'.$_SERVER["HTTP_ORIGIN"]);
}
header('Access-Control-Allow-Methods:OPTIONS, GET, POST');
header('Access-Control-Allow-Headers:x-requested-with');
header('Access-Control-Max-Age:86400');
// 功能...
// ...
?>
好了,上傳完代碼,繼續(xù)測(cè)試。發(fā)送請(qǐng)求之后,又報(bào)錯(cuò)了(這錯(cuò)中錯(cuò),一個(gè)個(gè)坑搞的大家都看得不耐煩了吧,我保證,這是最后一個(gè)報(bào)錯(cuò)了)
XMLHttpRequest cannot load http://120.111.111.123/setcookie.php.
Response to preflight request doesn't pass access control check: Credentials flag is 'true', but the 'Access-Control-Allow-Credentials' header is ''.
It must be 'true' to allow credentials. Origin 'null' is therefore not allowed access.
大概的意思就是說(shuō)我給你發(fā)了withCredentials報(bào)文頭,但是你服務(wù)器沒(méi)有跟我說(shuō)允許我?guī)н@個(gè)報(bào)文頭,那么解決方法就是加上允許發(fā)這個(gè)報(bào)文頭的報(bào)文頭
# path setcookie.php
<?php
session_start();
// 是否存在請(qǐng)求源
if(isset($_SERVER["HTTP_ORIGIN"])) {
header('Access-Control-Allow-Origin:'.$_SERVER["HTTP_ORIGIN"]);
}
header('Access-Control-Allow-Origin:null');
header('Access-Control-Allow-Methods:OPTIONS, GET, POST');
header('Access-Control-Allow-Headers:x-requested-with');
header('Access-Control-Max-Age:86400');
header('Access-Control-Allow-Credentials:true');
// 功能...
// ...
?>
接下來(lái)進(jìn)行最終的測(cè)試,biu~成功了,終于成功了!!!(0.0自己嗨起來(lái)了)
接下來(lái)總結(jié)一下,之所以跨域會(huì)引起那么多問(wèn)題,都是因?yàn)楣⒅钡目蛻?hù)端,發(fā)什么類(lèi)型的請(qǐng)求都要服務(wù)器允許,而且要明文允許,允許的內(nèi)容包括如下
1.跨域允許
解決方法:服務(wù)器發(fā)送允許客戶(hù)端發(fā)送源的報(bào)文頭
header('Access-Control-Allow-Origin:'.$_SERVER["HTTP_ORIGIN"]);
2.客戶(hù)端無(wú)法攜帶跨域cookie
這個(gè)時(shí)候就可以在extjs中加入withCredentials
Ext.Ajax.request({
url: 'http://120.111.111.123/setcookie.php',
method: 'POST',
params: {
'text': 'hello world'
},
withCredentials: true,
success: function(transport){
// do something
},
failure: function(transport){
alert("Error: " - transport.responseText);
}
});
3.因?yàn)榧恿藈ithCredentials報(bào)文頭,可是客戶(hù)端不知道服務(wù)器允不允許報(bào)的錯(cuò)(耿直的客戶(hù)端)
這個(gè)時(shí)候就在服務(wù)器發(fā)送Access-Control-Allow-Credentials
header('Access-Control-Allow-Credentials:true');
4.由于客戶(hù)端不知道服務(wù)端是否允許POST請(qǐng)求而報(bào)的錯(cuò)
這個(gè)時(shí)候要在服務(wù)器端加入
header('Access-Control-Allow-Methods:OPTIONS, GET, POST');
header('Access-Control-Allow-Headers:x-requested-with');
header('Access-Control-Max-Age:86400');
以上匯總起來(lái)就是
header('Access-Control-Allow-Methods:OPTIONS, GET, POST');
header('Access-Control-Allow-Headers:x-requested-with');
header('Access-Control-Max-Age:86400');
header('Access-Control-Allow-Origin:'.$_SERVER['HTTP_ORIGIN']);
header('Access-Control-Allow-Credentials:true');
header('Access-Control-Allow-Methods:GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers:x-requested-with,content-type');
header('Access-Control-Allow-Headers:Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With');
代碼已經(jīng)放到github了,有啥問(wèn)題歡迎大家指出