最近在工作中遇到这样的需求,新用户在第一次登录的时候需要强制修改密码。刚开始接到这个需求的时候,心里想着在axios的请求拦截器中统一处理就可以了,判断用户是否需要修改密码,如果需要修改密码就返回一个reject,从而不发送请求。好像很简单啊,说干就干。
首先照着上面的想法,撸出下列代码:
http.interceptors.request.use(config => {
if (needChangePassword) {
return Promise.reject(new Error('changePassword'))
} else {
config.cancelToken = store.source.token
return config
}
}, err => {
return Promise.reject(err)
})
心里想着,如果需要修改密码,就调用reject,然后在每个发送请求的外面都会有catch异常,在catch中统一处理错误信息,so beautiful,保存->运行,唉唉唉,怎么reject之后跑到外面的then里面去了,不是应该跳catch吗?有毒啊。
各种debugger之后,发现无论怎么reject,就是不跳catch。在走投无路之下,只能求助源码了,最后在源码core->Axios.js中发现了promise的调用顺序,主要源码如下:
var chain = [dispatchRequest, undefined];
var promise = Promise.resolve(config);
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
chain.unshift(interceptor.fulfilled, interceptor.rejected);
});
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
chain.push(interceptor.fulfilled, interceptor.rejected);
});
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
看完之后,才不得不感叹下,大牛果然就是不一样。源码的大致意思是这样的,首先处理request拦截器相关的promise,然后再发送请求,然后再处理respose拦截器相关promise,通过一个while循环生成了一个promsie链,最后返回的promise相当于下面:
promise = promise.then(request.fuifilled1, request.reject1)
.then(request.fuifilled2, request.reject)...
.then(dispatchRequest, undefined)
.then(response.fuifilled1, response.reject1)
.then(response.fuifilled2, response.reject2)...
.then(response.fuifilledn, response.rejectn)
这个chain真是用的好,又学习到了。最后回到上面的问题,应该就很简单了,在request的fuifilled中reject,然后到undefined,然后到response中的reject。由于最开始没有在response的reject处理changePassword的错误,并且没有在reject中始终返回reject,所有就跑到外面的then里面,而没有跳到catch中。
最后附上修改后的代码:
http.interceptors.request.use(config => {
if (needChangePassword) {
return Promise.reject(new Error('changePassword'))
} else {
config.cancelToken = store.source.token
return config
}
}, err => {
return Promise.reject(err)
})
// 配置拦截器与错误处理
http.interceptors.response.use(function (response) {
const code = _.get(response, 'data.code')
if (code !== 10000) {
if (code === 10008) {
window.location.href = '/'
} else {
isDev && console.error(response, '请求返回状态码不为10000')
throw new Error(response.data.message)
}
} else {
return response.data.data ? response.data.data : response.data
}
}, function (error) {
if (axios.isCancel(error)) {
throw new Error('cancelled')
} else if (error.message === 'changePassword') {
throw new Error('changePassword')
} else {
if (error.response) {
if (isDev) {
console.group('请求发生错误')
console.log(error.response.data)
console.log(error.response.status)
console.log(error.response.headers)
console.groupEnd()
}
throw new Error('接口异常, 请联系管理员')
}
}
})
最后感叹下,源码还是个好东西,既能加深理解,又能学习知识。
网友评论