問題描述
fetch 傳送一個請求,請求登入過期返回 302,瀏覽器自動重定向到 Response Headers 的 Location 登入頁面。Location 對應的伺服器不接受跨域請求,因此頁面報錯。
嘗試在 fetch 的回撥函式處理,一旦 fetch 的 response status 是 302 就跳轉到 Location 頁面, 但是不論在fetch的回撥函式中做任何事情,都執行不到。
什麼是 HTTP 狀態碼 302 ?
HTTP 302 重定向狀態碼錶明請求的資源被暫時的移動到了由Location
頭部指定的 URL 上。瀏覽器會重定向到這個URL, 但是搜尋引擎不會對該資源的連結進行更新。
使用場景:
- 在 OAuth 流程中,302 經常使用;
- 有時候請求的資源無法從其標準地址訪問,但是卻可以從另外的地方訪問。在這種情況下可以使用臨時重定向。
- 搜尋引擎不會記錄該新的、臨時的連結。在建立、更新或者刪除資源的時候,臨時重定向也可以用於顯示臨時性的進度頁面。
痛點:瀏覽器 自動 發起對 重定向地址的請求,js 無法插手干預。
fetch 為什麼不能攔截302?
一般請求的流程:
- fetch 傳送請求;
- 伺服器返回 response 並且帶有狀態碼(比如200) ;
- 瀏覽器接收到響應,結果遞交給fetch;
- 從 fetch 的回撥函式獲取相應資料;
302 臨時重定向請求的流程是:
- fetch 傳送請求;
- 伺服器返回 response (帶有location) 並且帶有 302 狀態碼;
- 瀏覽器接收到響應,自動從302響應的頭資訊的重定向地址中取到 location 進行跳轉;
對於重定向,當瀏覽器檢查到 headers 中存在 Location,會直接進行跳轉,不會告知任何請求傳送者(fetch),這時候傳送者會以為請求還在處理中。所以此時的 fetch 的 then 和catch 都捕獲不到資訊
如何解決?
1. 配置 fetch 的 redirect
fetch 的 options 配置項redirect
,用於配置可用的 redirect 模式。
redirect 的值有:
- follow:預設, 自動重定向;
- error:如果產生重定向將自動終止並且丟擲一個錯誤;
- manual:手動處理重定向;
error
如果產生重定向將自動終止並且丟擲一個錯誤。此錯誤可以在 fetch catch 回撥函式中捕獲:TypeError: Failed to fetch
。
fetch 只有伺服器錯誤才呼叫 catch,其他都會呼叫 then 函式,那麼 302 為什麼會呼叫catch?
不是 302 導致 catch 被呼叫而是重定向後的請求的 response 導致 catch 被呼叫。
manual
手動處理重定向。通過這種方法只能知道發生了重定向,但是 response 的內容非常有限,無法獲取到具體的資訊。
2. 後端改寫狀態碼,前端手動處理
目前的 302 是對 404 的改寫,那麼如果我們將 404 改寫成自定義狀態碼,然後前端捕獲到這個狀態碼後,進行手動處理。這種方法則需要前後端的同學都做處理。
301 和 302 狀態碼區別
301:永久重定向。一旦請求發往某個URL,狀態碼返回301,那麼瀏覽器就會自動跳轉到 header中 Location 對應的 url。下次請求,再次向 location 對應的 url 傳送請求。
- 之後每次請求都會跳轉到 location 對應的url。沒有例外。
- 瀏覽器可以快取從這個 url 獲取的響應。
302:臨時重定向。請求的資源臨時從不同的url獲取。一旦請求發往某個URL,狀態碼返回302,那麼瀏覽器就會自動跳轉到 header中 Location對應的 url。但是下次再次請求的時候向原來的url發請求。
- 每次請求不能確定是否向 Location 的 url 發請求,因此需要先想原來的 url 傳送請求確定。
- 瀏覽器不可快取從重定向的 url 獲取到的響應。
重定向可以用來set cookie
瀏覽器如果發現當前請求的響應要重定向,則會直接忽略掉 response 的 body,無法在開發者工具中的 network 皮膚上看到 body。雖然重定向請求的 body 會被瀏覽器忽略掉,但重定向請求響應的頭部仍然可以發揮作用。
例如一個請求服務端返回 302 的同時 set-cookie,那麼瀏覽器可以在發起跳轉之前在當前頁面的域下set cookie。即使因為 302 跳轉到其他域了,也仍然可以set cookie。
- 使用者訪問 a 域名
- 後端返回302,location是 b 域名,同時set-cookie: cookieA
- 瀏覽器在 a 域名下種上cookie: cookieA,然後向 b 域名發起請求
- 後端返回 302,location 是a 域名,同時 set-cookie: cookieB
- 瀏覽器在 b 域名下種上cookie: cookieB,然後向 a 域名發起請求
這裡相當於是實現了這樣一種效果:一次請求即可向不同域名種下cookie(重定向是後端控制的,前端透明,相當於只有一次請求的效果)。
如果不用重定向的話,可能會考慮配置CORS發一個跨域請求set cookie,但CORS一旦涉及到cookie這種 credentials 資訊,就會出現各種各樣的限制,實際很難發揮效果。
其實跨域 set cookie 還可以用瀏覽器的 beacon API 實現,當然也是有一些限制的
重定向可以有多次,比如連續的302, 就是 location 對應的 URL 又返回了 302 和新的location,如此重複直到不再跳轉位置。為了防止出現無限重定向的情況,重定向的次數是有上限的。Chrome 瀏覽器的重定向次數是20,超過20次重定向就會報
ERR_TOO_MANY_REDIRECT
錯誤。