詳解Axios統一錯誤處理與後置

問題前端

在進行業務開發的時候,先後端會對接口的數據結構進行約定,若接口有異常,須要將異常信息展現給用戶知曉。這個流程裏,數據結構是肯定的(事先約定),數據的處理邏輯是相同的(展現給用戶),若是在業務代碼代碼中重複的catch(e) { 展現給用戶 },就很是的不優雅。本着Don't repeat myself(懶)的原則,須要對接口錯誤進行統一處理。ios

接下來,我會結合具體的業務場景,講一講個人解決方案。element-ui

業務場景axios

前端精品教程:百度網盤下載後端

  1. 後端經過http狀態標識接口狀態,錯誤信息在response的data裏
  2. 前端的處理邏輯是使用element-ui的Message展現錯誤信息
  3. 使用axios

axios能夠經過攔截器,在業務代碼處理響應以前對響應進行處理,相似於下面的流程promise

?
1
2
3
someAPI()
   .then(interceptorsFn)
   .then(業務邏輯)

因此,咱們能夠在interceptors對響應進行統一處理:數據結構

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
request.interceptors.response.use(
   (response) => response.data,
   (error) => {
     // 針對特定的http狀態碼進行處理
     if (error.response && error.response.status === 401) {
       router.push({ name: 'ssoLogin' })
       return new Promise(() => {}) // pending的promise,停止promise鏈
     }
 
     .....
 
     const msg = error.response.data
     Message.error(msg)
 
     return Promise.reject(error.response)
   }
)

如何進行特定的錯誤處理框架

前端精品教程:百度網盤下載ide

不難看出,上面的方案有一個問題,若是有某個接口須要有業務代碼來展現定製的錯誤信息(這個狀況十分常見),如何處理?函數

naive方案1:業務代碼使用其它的方式展現信息:例如Notify。
這個方案被我司產品痛罵,由於破壞了統一的錯誤信息展現,而且此時統一的錯誤信息是一個垃圾信息,不必展現。

naive方案2:業務代碼直接使用Message,頂掉統一的錯誤信息。
這個方案仍是被產品大哥(dog)懟了,由於明顯的用戶體驗很差,錯誤信息出現了閃爍。

帥氣的解決方案3:業務代碼決定是否隱藏統一錯誤提示
那麼問題來了,因爲是先走攔截器,再走業務代碼,如何由業務代碼決定是否隱藏統一錯誤提示呢?

個人辦法是,將統一的錯誤提示使用setTimeout放到下一個loop執行,並經過一個變量標識是否要執行統一錯誤提示。

?
1
2
3
4
5
6
7
8
9
10
11
request.interceptors.response.use(
   (response) => response.data,
   (error) => {
     ...
     setTimeout(() => {
       if (tag) {
         Message.error(msg)
       }
     })
   }
)

接下來,須要考慮的是,如何在業務代碼裏改變標識變量

naive方案1:一個全局的變量或者方法

這個方案很是的不靠譜,若在其它代碼裏改變了這個全局變量,就嗝屁,而且N個接口公用一個標識變量,只能是同一個狀態。

帥氣方案2:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
request.interceptors.response.use(
   (response) => response.data,
   (error) => {
     ...
     let isShowNormalError = true
     const hideNormalError = () => isShowNormalError = false
 
     setTimeout(() => {
       if (isShowNormalError) {
         Message.error(msg)
       }
     })
 
     return Promise.reject({ ...error.response, hideNormalMessage }) // 在error.response上添加方法
   }
)

業務代碼:

?
1
2
3
4
5
6
someAPIFN()
   .then()
   . catch ({ data, hideNormalMessage }) {
     // 業務代碼
     hideNormalMessage()
   }

兼容舊代碼

前端精品教程:百度網盤下載

目前的方案須要對現存代碼作修改,對進行特殊處理的接口添加hideNormalMessage()。若是不想全局搜索添加代碼(懶),能夠根據業務來進行兼容。下面講一下我結合業務代碼進行的兼容處理(很是不推薦)。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
request.interceptors.response.use(
   (response) => response.data,
   (error) => {
     // warning,和業務代碼深度耦合,不推薦
     const hasMessageBeforeCatch = !!document.querySelector( '.el-message' )
     
     ...
 
     let isShowNormalError = true
     const hideNormalError = () => isShowNormalError = false
 
     setTimeout(() => {
       const hasMessageAfterCatch = document.querySelector( '.el-message' )
 
       // 調用catch前沒有message,調用catch後有message,表示message是在catch過程當中產生
       const madeMessageWhenCatch = !hasMessageBeforeCatch && hasMessageAfterCatch
 
       if (isShowNormalError && !madeMessageWhenCatch) {
         Message.error(msg)
       }
     })
 
     return Promise.reject({ ...error.response, hideNormalMessage }) // 在error.response上添加方法
   }
)

邏輯:若是在catch中使用了Message,就不展現統一錯誤處理

總結

這個解決方案的關鍵在於使用setTimeout使得統一錯誤處理「落後」於業務代碼,並在Promise.reject的參數中添加控制函數使得業務代碼能夠決定是否展現統一錯誤處理。稍做抽象與封裝就能夠造成一個業務無關、框架無關的統一錯誤處理方案。

相關文章
相關標籤/搜索