由于微信小程序登录态也就是token会失效,所以在请求后台接口的时候会返回401,需要重新调用登录接口获取刷新token才能继续进行。碰到未授权的情况可以有以下几种处理方案:
- 直接跳转会登录页,让用户重新登录。
- 在页面显示重试按钮,让用户点击重试。
- 接口返回401后直接调用登录接口刷新token后再重新请求直接的接口。
在用户体验方面第三种的方式固然是最好的,然后碰到的坑也多……
直接上代码
...
if (res.statusCode === 401) {
console.log(401)
if (res.requestObj.reTryCount === 3) {
console.log('401重试3次')
wx.showToast({
title: '系统异常,请稍后再试',
icon: 'none',
image: '',
duration: 2000,
mask: true,
})
throw res
}
Promise.resolve()
.then(() => login())
.catch(error => {
console.log(error)
})
const reRequest = () => {
const _count = reTryCount + 1
return request(
...res.requestObj,
_count)
}
return LOGIN_INSTANCE ? LOGIN_INSTANCE.then(()=>reRequest()) : reRequest()
}
...
这是我的第一次尝试,在重试请求的同时还加入了重试次数,超过3次就显示异常。然而这个解决方案有个问题,如果有两个并发请求A和B,同时返回401未授权,这时会同时登录,存在网络波动的情况下,B接口可能先重新登录成功,刷新了token,在重新尝试调用B接口时,A接口重新登录成功,刷新了token。接着又是401…401…。当然存在正常按照顺序执行的情况,但更多的是会多刷新几遍token,也就是会多执行几次登录接口。为了解决这个问题,继续优化代码。
let isRefreshing = false
/* 存储请求的数组 */
const refreshSubscribers = []
/* 将所有的请求都push到数组中,其实数组是[function(token){}, function(token){},...] */
function subscribeTokenRefresh(cb) {
refreshSubscribers.push(cb)
}
...
if (res.statusCode === 401) {
// 缓存401的请求,在更新token后再重新执行
subscribeTokenRefresh(() => {
return request(...res.requestObj)
})
if (!isRefreshing) {
isRefreshing = true
Promise.resolve()
.then(() => login())
.then(() => {
// 依次执行缓存数组中的请求
while (refreshSubscribers.length > 0) {
const fn = refreshSubscribers.shift()
try {
fn && fn()
} catch (e) {
throw e
}
}
isRefreshing = false
})
.catch(e => {
throw e
})
}
}
...
这次的优化是将请求缓存起来,在刷新后再重新执行。这样只会调用一次登录。