最近项目中遇到 axios 异步请求异常中断, 错误码为 "ECONNABORTED"
奇怪的是排查前端代码并没有发现有主动调用 abort 取消请求的
由于为何网络请求失败的原因找不到, 但是重试请求就是成功的, 所以计划使用 axios-retry 在网络错误时重新请求
javascript
import axiosRetry from 'axios-retry'
import axios from 'axios'
const service = axios.create({
// .... 各种配置项
})
axiosRetry(service, {
retries: 1, // 重新请求次数, 这里由于只是为了解决特殊情况下的网络错误, 所以只重试一次即可
// 重试条件
retryCondition: error => {
return error.code === 'ECONNABORTED' // 仅这个错误下重试请求
}
})
使用以上方案后, 解决了网络错误下接口失败的问题, 但是发现在重试请求成功后, js 代码里拿到的结果不对
经过排查, 发现 axios-retry 虽然能重新请求, 但是会基于实际请求次数多次调用 interceptors 拦截器的钩子
拦截器代码
javascript
service.interceptors.response.use(
response => {
return response.data
},
error => {
ElMessage({
message: error.message,
type: 'error',
duration: 3000
})
}
)
拦截器钩子第二次执行时返回的实际上是 response.data.data, 这会导致外部调用者拿不到预期结果 但这是 axios 或 axios-retry 内部的问题, 我们无法解决, 所以只能在钩子里做判断, 如果当前请求已经进入过拦截器处理, 就直接退出
拦截器做调整
javascript
service.interceptors.response.use(
response => {
// 如果之前进入过拦截器钩子, 那么 response 实际上就已经是 response.data, 直接 return
if(!(response.request instanceof XMLHttpRequest)){
return response
}
return response.data
},
// 错误处理加入防抖, 同样的 error 只执行一次回调
throttleErrorCallback(error => {
ElMessage({
message: error.message,
type: 'error',
duration: 3000
})
})
)
// 错误处理防抖
function throttleErrorCallback(callback){
const handledErrors = new WeakSet()
return error => {
if(handledErrors.has(error) {
return Promise.reject(error)
}
handledErrors.add(error)
return callback(error)
}
}
如果有其他解决 axios-retry 带来的拦截器问题的方式, 欢迎交流