问题:所有请求等待token
很多项目登陆使用的是前端传token,后端认证的方式,这就需要前端登陆后需要请求包括token等等的用户信息,将token放置到所有请求的header中,每一次请求都需要将token发到后台去。
这时候就需要进行处理,如何让其他所有请求,都能等待获取token等用户信息的这次请求之后再请求呢?
有很多种方法可以实现,例如,在vue项目中,我们可以在App.vue中加入一个flag,控制路页面组件的显示,在App.vue中获取token,当这个请求完成前,子页面都不显示,,自然里面的接口都不会调用,这个请求完成后,再将页面显示出来。
这样做的好处是方便,可是这就给用户人为增加了一段白屏时间,毕竟我们的网页有部份内容也不需要请求接口也可以展示。
解决:使用拦截器和promise
解决的思路是设立一个flag,专门识别获取token的请求是否已经完成,在这个请求完成前,其他请求都需要拦截保存下来不发送,等待获取token的请求回来后,再将保存的请求全部执行。
为了在请求接口上动手脚,我们需要用到axios的拦截器,而请求的拦截与保存我们需要用到promise,只要他不执行resolve,就是永远处于等待着的状态,直接上代码。
// 请求获取token接口的标记,0未请求,1正在请求,2已请求
let isGotToken = 0;
// 请求队列
let requests = [];
let token = ''
// 请求拦截器
axios.interceptors.request.use(
async config => {
if (isGotToken == 2 || config.url.includes("/get/token")) {
// info已经请求或者正在请求token接口
token && (config.headers.authorization = token);
return config
} else {
//获取token接口没有请求过
if (!isGotToken) {
// 正在获取
isGotToken = 1
try {
//获取token和用户信息的请求,保存到vuex中
token = await getToken()
} catch (e) {
}
isGotToken = 2
token && (config.headers.authorization = token);
// 把原来保存的请求重新执行,记得把刚刚请求到的token传进去
requests.forEach((cb) => cb(token));
requests = [];
// 以下为正在拦截的请求,没有被队列保存下来,所以重新return出去单独处理
return config
} else {
// info没有请求,保存所有请求等待token回复,只要没有执行resolve就一直在等待
return new Promise(resolve => {
requests.push((token) => {
token && (config.headers.authorization = token);
resolve(config);
})
});
}
}
}
当然可以把token本身当作是否请求过token的flag, 下面是前后的对比:
改造之前:
改造之后:
所有的接口都会等待info请求(获取token)返回后再开始请求。