js網絡請求跨域問題彙總(攜帶cookie)

js網絡請求跨域問題彙總(攜帶cookie)

前端程序使用extjs寫,在本地測試,發送請求到服務器時,發現存在跨域的問題,cookie也沒有set成功,於是乎在這裏整理一下解決過程

由於篇幅較長,不想看解決過程的可以翻到最後看總結
1.跨域允許
2.客戶端無法攜帶跨域cookie
3.因爲加了withCredentials報文頭,可是客戶端不知道服務器允不允許報的錯
4.由於客戶端不知道服務端是否允許POST請求而報的錯

假設我的服務器IP是120.111.111.123

# 本地的html
# index.html

<html>
<head>
    <meta charset="utf8">
</head>
<body>
    <input type="button" onclick="request()" value="請求">
</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>
#服務器的php文件
#path setcookie.php
<?php
session_start();
?>

點擊“請求”按鈕,發送請求後發現js報錯

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.

報這個錯就說明我們跨域了,不在允許的訪問源,於是乎我在服務的setcookie.php加入header('Access-Control-Allow-Origin:*');允許所有源

<?php
session_start();
header('Access-Control-Allow-Origin:*'); 

// 功能...
// ...

然後又報錯

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.

這次的報錯是因爲,在跨域的時候,extjs不會直接發post請求,而是先發送一個option請求,看看服務器允許什麼訪問頭(比如是不是允許post請求),驗證成功後纔會發送真正的請求

#用谷歌的開發者工具抓的option報文
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

接下來,我們只要發送我們允許什麼請求頭就行了

#path /setcookie.php

session_start();
header('Access-Control-Allow-Origin:*'); 

header('Access-Control-Allow-Methods:OPTIONS, GET, POST'); // 允許option,get,post請求
header('Access-Control-Allow-Headers:x-requested-with'); // 允許x-requested-with請求頭
header('Access-Control-Max-Age:86400'); // 允許訪問的有效期

// 功能...
// ...

繼續測試我們的新功能,成功的解決了跨域問題

1.png

but,cookie沒有“設置成功”。而之所以沒有“設置成功”,是因爲cookie存在本地,但是每個cookie都有一個domain,當你本地的cookie中存在你當前訪問的域時,纔會被帶過去,而我的index.html文件是本地訪問的,即http://localhost/index.html,而cookie的域是120.111.111.123的,所以不行了。於是乎繼續改
#path index.html
<html>
<head>
    <meta charset="utf8">
</head>
<body>
    <input type="button" onclick="request()" value="請求">
</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, # 加了這個
            success: function(transport){
                // do something
            },
            failure: function(transport){
                alert("Error: " - transport.responseText);
            }
        });
    }
    
</script>
</html>

繼續訪問,報錯

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.

現在這個錯誤產生的原因就是
1.因爲加入了withCredentials之後,Access-Control-Allow-Origin就不能用“*”了,既然不允許訪問這個源,那我就讓你發個報文頭讓你允許訪問唄!

<?php
#path setcookie.php
session_start();
// 是否存在請求源
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');

// 功能...
// ...

?>

好了,上傳完代碼,繼續測試。發送請求之後,又報錯了(這錯中錯,一個個坑搞的大家都看得不耐煩了吧,我保證,這是最後一個報錯了)

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.

大概的意思就是說我給你發了withCredentials報文頭,但是你服務器沒有跟我說允許我帶這個報文頭,那麼解決方法就是加上允許發這個報文頭的報文頭

# path setcookie.php
<?php

session_start();
// 是否存在請求源
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');


// 功能...
// ...

?>

接下來進行最終的測試,biu~成功了,終於成功了!!!(0.0自己嗨起來了)

2.png

接下來總結一下,之所以跨域會引起那麼多問題,都是因爲耿直的客戶端,發什麼類型的請求都要服務器允許,而且要明文允許,允許的內容包括如下
1.跨域允許
解決方法:服務器發送允許客戶端發送源的報文頭
header('Access-Control-Allow-Origin:'.$_SERVER["HTTP_ORIGIN"]);

4.png

2.客戶端無法攜帶跨域cookie
這個時候就可以在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.因爲加了withCredentials報文頭,可是客戶端不知道服務器允不允許報的錯(耿直的客戶端)
這個時候就在服務器發送Access-Control-Allow-Credentials

header('Access-Control-Allow-Credentials:true');

4.由於客戶端不知道服務端是否允許POST請求而報的錯
這個時候要在服務器端加入

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:*');
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');
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章