一、ajax 302
ajax用於異步獲取服務器數據,但是某天有這么一個使用場景:
> 基於安全考慮,登錄的用戶的信息失效時,系統的所有ajax接口都由服務器直接重定向到系統的登錄頁面,此時登錄頁面與系統屬於同一個域;
於是否,使用ajaxComplete的方法為系統所有ajax統一設置請求完成后的回調,判斷其http的status code是否為302,類似於下面代碼:
$(document).ajaxComplete(function(e, xhr, settings){
var _location;
console.log(xhr);
if(xhr.status === 302){
_location = xhr.getResponseHeader("Location");
if(_location) {
location.assign(_location);
}
}
});
//獲取數據的接口
$.ajax({
url: "/api/fetch.html"
}).always(function(ret){
console.log(ret);
});
可是測試結果顯示,ajax的回調在重定向之后死活沒有執行,直至獲取重定向后的請求才會執行ajax的回調,也就是說重定向后的內容作為ajax的接口內容來響應
。此時可以看到瀏覽器控制台輸出的ret
內容不是json字符串,實際上為系統重定向到登錄頁面的html字符串。
為什么會出現這樣的結果呢?
在stackoverflow上可以找到討論的答案:
> You can't handle redirects with XHR callbacks because the browser takes care of them automatically. You will only get back what at the redirected location.
也就是說,重定向是由瀏覽器自動透明的完成的。所以服務器將302響應發給瀏覽器時,瀏覽器並不是直接處理ajax的回調,而是先執行302重定向。這就是上面例子中為什么獲取不到xhr.status為302的值。
一個ajax請求的重定向大致流程是這樣的:
ajax --> browser --> server --> 302 --> browser(redirect) --> server --> browser --> ajax callback
注意,上面ajax獲取不到xhr的status是有一個前提:即,服務器為response設置了*Location* header
。
因為,瀏覽器在發現Location的header時就會自動跳轉到Location所指定的URL地址,類似於用js來進行重定向;不過這個重定向只有瀏覽器知道。
所以,在ajax接口返回302時,而沒有設置Location的header時,這個xhr的status值還是能獲取到的,下圖是在瀏覽器控制台測試的結果:

可以看到服務器給response的header添加了Location1的header,瀏覽器並不認識,所以不會重定向,此時或可以獲取xhr的status為302
二、ajax 302跨域
隨着公司業務的發展新的情況出現了,公司要將登錄認證統一到某台公共的服務器上,這時,失效的用戶在ajax獲取數據時狀態失效而被重定向到登錄頁面;由於當前系統與登錄頁面不同域,而XHR不允許跨域訪問數據,所以ajax的302也存在跨域問題。(不像頁面之間重定向不存在這個問題,你可以訪問當前系統的某個頁面,服務可以重定向到www.baidu.com而不會有跨域問題)。
這時,由於存在跨域,瀏覽器不會進行重定向;但是能否通過上面的代碼來用JavaScript進行重定向呢?
抱歉,同樣不可以;這時通過ajaxComplete方法獲取到的xhr的status值,發現其值為0,Location的header為null;所以這種辦法行不通。更可取的方法可以是這樣:
首先不要在服務器端進行重定向;然后通過接口的形式或者在response添加一個自定義header來區分重定向信息;最后由JavaScript來進行重定向
三、xhr status為0的問題
翻看W3C文檔,發現xhr的status值由以下三個步驟來決定:
The status attribute must return the result of running these steps:
1. If the state is UNSENT or OPENED, return 0.
2. If the error flag is set, return 0.
3. Return the HTTP status code.
第二條的error flag:The error flag indicates some type of network error or fetch termination. It is initially unset.
通過上面得知:
在調用xhr的open方法還沒有調用send方法,或者xhr出錯了(如跨域就會發生網絡錯誤)都會導致xhr state為0的情況
最常見的兩種xhr status為0的情況:
-
ajax發送后還沒有得到響應前立即刷新瀏覽器,這時ajax就會被瀏覽器給丟棄了,會返回status code為0;這主要發生在form表單的提交用ajax來提交,而沒有阻止表單提交的默認行為,導致頁面刷新,這時ajax發出了,但頁面刷新; 其實ajax能得到xhr的status只不過值為0;
-
xhr跨域,包括異步請求跨域和302跨域的情況,這時會出現xhr status為0的情況