前端項目中使用 axios 請求接口,基于 axios 二次封裝了一些業務邏輯,一般我們會在請求和響應攔截器裡添加自己項目相關的業務邏輯,一個簡單的 demo 如下:
import axios from 'axios'
import config from '@/config'
import cookies from 'vue-cookies'
axios.defaults.baseURL = config.apiBaseURL
// 請求攔截
axios.interceptors.request.use(
config => {
config.timeout = 600000
let token = cookies.get('token')
if (token) {
config.headers.token = token
}
config.headers = Object.assign(config.headers, {
'Content-Type': 'application/json'
})
return config
},
error => {
return Promise.reject(error)
}
)
// 響應攔截
axios.interceptors.response.use(
function(response) {
// 2xx 範圍内的狀态碼都會觸發該函數
return response.data || {}
},
function(error) {
// 超出 2xx 範圍的狀态碼都會觸發該函數
return Promise.reject(error)
}
)
export default axios
正常我們項目中使用是沒問題的,比如在 vue 項目中使用:
<script>
export default {
created() {
this.getData()
},
methods: {
async getData() {
const res = await this.$axios({
method: 'get',
url: 'user'
})
if (res.success) {
console.log('success')
} else {
console.log('error')
}
}
}
}
</script>
但是有一天後端告訴你,我們架構在外層直接做了參數校驗、登入校驗...就是如果各種亂七八糟的校驗不通過時,http狀态碼就不是我們正常見到的 200 喲,可能登入态 token 失效直接扔給前端一個 401。然後問後端可不可以改下,這種情況應該也是 200 才對,因為請求已經成功,然後具體的你們的校驗邏輯可以放在裡層資料的 code 裡,前端也比較好處理。
後端說實作不了,這是架構做的,壓根還沒請求到具體的服務裡,好吧,那隻能自己動手豐衣足食了。
像這種非 2xx 的狀态碼,axios 的相應攔截器會直接走第 2 個 error,封裝的是直接傳回 Promise.reject(error),這樣前端如果不去做任何處理的話,控制台會報錯:Uncaught (in promise),也就是有一個異常未去捕捉。
要解決這個報錯就需要前端在具體的調用地方用 catch 或者 try catch 去捕捉,如果是直接在 await 的後面 catch 捕捉,報錯确實消失了,但是 await 的傳回值 res 此時就是 undefined,此時 await 接收到的值就是 catch 裡的傳回值,如果沒有 return 就是 undefined 了。
然後悲催地跟着後面如果有用 res 做判斷,又會報錯:TypeError: Cannot read properties of undefined...如果還不明白,可以看下面的小 demo:
async test() {
const res = await new Promise((resolve, reject) => {
setTimeout(() => {
reject('fail')
}, 3000)
})
// 報錯:Uncaught (in promise)
// 下面的代碼不會執行
console.log('res:', res)
if (res.success) {
console.log('success')
}
},
async testCatch() {
const res = await new Promise((resolve, reject) => {
setTimeout(() => {
reject('fail')
}, 3000)
}).catch(error => console.log(error))
console.log('res:', res) // undefined
if (res.success) { // 報錯:TypeError: Cannot read properties of undefined
console.log('success')
}
},
最終實作方式
頁面裡用 try catch 或者自己多加幾個判斷,确實能解決報錯,但是好像也不太優雅,目前找到的比較好的方式,如果能夠拿到 error 裡的一些報錯資訊,自己模拟組裝一份資料,直接 return 給前端,這樣既不用額外 catch,也解決了後續的報錯問題:
import axios from 'axios'
import config from '@/config'
import cookies from 'vue-cookies'
axios.defaults.baseURL = config.apiBaseURL
// 請求攔截
axios.interceptors.request.use(
config => {
config.timeout = 600000
let token = cookies.get('token')
if (token) {
config.headers.token = token
}
config.headers = Object.assign(config.headers, {
'Content-Type': 'application/json'
})
return config
},
error => {
loadingInstance && loadingInstance.close()
if (error.response && error.response.status == 401) {
window.location.href = '/login'
}
return Promise.reject(error)
}
)
// 響應攔截
axios.interceptors.response.use(
function(response) {
// 2xx 範圍内的狀态碼都會觸發該函數
return response.data || {}
},
function(error) {
// 超出 2xx 範圍的狀态碼都會觸發該函數
let errMsg
const response = error.response
if (response) {
onst data = response.data || {}
const dataMsg = data.message || data.msg
const statusText = response.statusText
errMsg = dataMsg || statusText || '出錯了'
} else if (error.message) {
errMsg = error.message
}
// 有錯誤資訊時自己模拟一份錯誤資料傳回,解決後端動不動就直接 http 狀态碼 500、401...
// 直接 reject 的話外層需要自己 catch,還需要額外判斷 await 的傳回值
if (errMsg) {
return {
message: errMsg,
success: false,
}
}
return Promise.reject(error)
}
)
export default axios