fetch 如何處理 302?

時傾發表於2022-01-19

問題描述

fetch 傳送一個請求,請求登入過期返回 302,瀏覽器自動重定向到 Response Headers 的 Location 登入頁面。Location 對應的伺服器不接受跨域請求,因此頁面報錯。

嘗試在 fetch 的回撥函式處理,一旦 fetch 的 response status 是 302 就跳轉到 Location 頁面, 但是不論在fetch的回撥函式中做任何事情,都執行不到。
wecom-temp-8251e18b8b181c526d372fc12166a5fd.jpg

什麼是 HTTP 狀態碼 302 ?

HTTP 302 重定向狀態碼錶明請求的資源被暫時的移動到了由Location頭部指定的 URL 上。瀏覽器會重定向到這個URL, 但是搜尋引擎不會對該資源的連結進行更新。

使用場景:

  1. 在 OAuth 流程中,302 經常使用;
  2. 有時候請求的資源無法從其標準地址訪問,但是卻可以從另外的地方訪問。在這種情況下可以使用臨時重定向。
  3. 搜尋引擎不會記錄該新的、臨時的連結。在建立、更新或者刪除資源的時候,臨時重定向也可以用於顯示臨時性的進度頁面。
痛點:瀏覽器 自動 發起對 重定向地址的請求,js 無法插手干預。

fetch 為什麼不能攔截302?

一般請求的流程:

  • fetch 傳送請求;
  • 伺服器返回 response 並且帶有狀態碼(比如200) ;
  • 瀏覽器接收到響應,結果遞交給fetch;
  • 從 fetch 的回撥函式獲取相應資料;

wecom-temp-14efb359973bd54833f4c2c6d72387c8.jpg

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 被呼叫。

wecom-temp-e8e8792e6097b4c56edc9e71a8a24345.jpg

manual

手動處理重定向。通過這種方法只能知道發生了重定向,但是 response 的內容非常有限,無法獲取到具體的資訊。
wecom-temp-4b5a5d7fd00b75c7e3e23296626a9a42.jpg

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。

  1. 使用者訪問 a 域名
  2. 後端返回302,location是 b 域名,同時set-cookie: cookieA
  3. 瀏覽器在 a 域名下種上cookie: cookieA,然後向 b 域名發起請求
  4. 後端返回 302,location 是a 域名,同時 set-cookie: cookieB
  5. 瀏覽器在 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錯誤。

相關文章