不能顯式攔截ajax請求的302響應?

部落格猿馬甲哥發表於2023-10-01

記錄工作中早該加深印象的一個小case: ajax請求不能顯式攔截 302響應。

我們先來看一個常規的登入case:

  1. 瀏覽器請求資源,伺服器發現該請求未攜帶相關憑據(cookie或者token)
  2. 伺服器響應302,並在響應頭Location寫入重定向地址, 指示瀏覽器跳轉到登入頁
  3. 瀏覽器跳轉到登入頁,提交身份資訊,回撥到原業務站點,服務端利用Set-Cookie響應頭種下cookie或者token

利用axios庫發起請求

Axios is a promise-based HTTP Client for node.js and the browser. It is isomorphic (= it can run in the browser and nodejs with the same codebase). On the server-side it uses the native node.js http module, while on the client (browser) it uses XMLHttpRequests.

When you make an HTTP request with axios, the library returns a promise. If the request is successful (i.e. the server responds with a 2xx status code), the promise will be resolved and the then() callback will be called with the response data. On the other hand, if the request fails (i.e. the server responds with a 4xx or 5xx status code), the promise will be rejected and the catch() callback will be called with the error.

  1. axios在瀏覽器發起的是ajax請求
  2. axios預設認為2xx狀態碼是成功的響應, 會進入promise的resolved回撥函式, 本case第一次會收到302重定向響應, 故新增ValidateStatus配置

虛擬碼如下:

   axios.request({
       method:'get',
       url:'/login',
       validateStatus: function (status) {
                return status >= 200 && status < 300 || status === 302; 
       },
   }).then((resp)=> {
     if resp.status ===302 {
         window.location.href = resp.headers['Location']
      }else{
        var userInfo = JSON.parse(
                    decodeURIComponent(
                        resp.data.msg || ""
                    ) || "{}"
                )
        this.setState({
              userInfo
        })
     }
   })

實際上以上ajax請求收到的302響應並不能被顯式攔截,上面的resp實際是redirect之後的頁面的響應體

核心在於:所有瀏覽器都遵循了ajax標準readystatus=2, All redirects (if any) have been followed and all headers of a response have been received.

翻譯下來就是 : ajax收到的響應如果有重定向,必然是重定向邏輯走完之後的響應。


對於這個常規的case, github上給出的思路是: 針對不同型別的http請求,服務端給出不同的狀態碼。

  if  req.isXhr {
     c.JSON(http.StatusForbidden, gin.H{
          "code": 403,
          "msg":  redirectUrl}) 
  }else {
      c.Redirect(http.StatusFound, redirectUrl)
  }

如果是ajax請求,返回4xx json響應,讓瀏覽器主動重定向。

 axios.defaults.headers.common['X-Requested-With']="XMLHttpRequest";
 axios.request({    
            method: 'get',
            url: '/login',
            validateStatus: function (status) {
                return status >= 200 && status < 300 || status === 403; 
            },
        }).then((resp)=> {
            if (resp.status===200  && resp.data.code === 200) {
               ......
            }else{
                window.location.href = resp.data.msg
            }
        })   

相關文章