项目要求: uniapp项目 vue2 ,微信,支付宝小程序 亲测有效。
需求 =>小程序静默授权之后进行登录,需要保证用户在任何操作过程中都不能让token过期,即过期时进行无感刷新。
核心思路 => 所有登录操作以及静默授权,以及刷新token操作都在响应拦截器进行统一处理,应用Promise队列的方式吧请求挂起,使用await阻塞请求的响应,保证在第一次请求失败后,拿到响应码,进行token的换取,拿到最新token之后把队列的请求逐个恢复。
提供给需要帮助的小伙伴,不足的地方欢迎指出。
项目依赖:使用第三方sdk luch-request ,自行导入项目可以使用npm,也可以直接把包clone放到项目文件夹进行导入。
代码部分
import Request from "@/js_sdk/luch-request/index.js"; //第三方sdk文件
import store from "@/store/index.js"; //封装的vuex action登录接口
const http = new Request();
// 是否正在刷新token的标记
let isRefreshing = false;
// 重试请求队列
let requests = [];
http.setConfig((config) => {
/* 设置全局配置 */
config.baseURL = '服务器路径'; /* 根域名不同 */
config.dataType = "string"; //响应体转成string模式(本人项目要求), 如果不需要可以去掉,默认是json
config.header = {
...config.header,
}; //解析请求头信息
return config;
});
/**
* 自定义验证器,如果返回true 则进入响应拦截器的响应成功函数(resolve),否则进入响应拦截器的响应错误函数(reject)
* @param { Number } statusCode - 请求响应体statusCode(只读)
* @return { Boolean } 如果为true,则 resolve, 否则 reject
*/
http.validateStatus = (statusCode) => {
return statusCode === 200;
};
http.interceptors.request.use((config, cancel) => {
if (config.url != "刷新token接口路径" ) {
uni.showLoading({
title: "加载中...",
});
} //为了登录或者刷新token的接口不开启Loading,根据自己项目需求删减。
/* 请求之前拦截器 */
const token = uni.getStorageSync("token");//获取token
if (token) {
config.header = {
...config.header,
"authorization": "Bearer " + token,
};
}
return config;
});
http.interceptors.response.use(
(response) => {
uni.hideLoading();
/* 请求之后拦截器 */
// const res = response.data
if (res.code === 1) {
//提示错误信息
uni.showToast({
title: res.msg,
duration: 1500,
});
return Promise.reject(response);
} else {
return res;
}
},
async (response) => {
switch (response.statusCode) {
case 424:
if (!isRefreshing) {
// 正在刷新
isRefreshing = true;
const accountInfo = uni.getAccountInfoSync(); //获取登录的appId
// #ifdef MP-WEIXIN
const res = await uni.login({
provider: "weixin",
onlyAuthorize: true,
}); //拿到微信的code
uni.clearStorageSync() //清除缓存信息
await store.dispatch("login", {
code: res[1].code,
appId:accountInfo.miniProgram.appId
});
//vuex 登录获取token接口,根据个人项目封装传参
// #endif
// 本项目兼容的平台过多,再此仅以微信小程序为例子
requests.forEach(async ({
fn,
resolve
}) => {
// 逐个按请求队列顺序重新发起请求
const ress = await fn();
resolve(ress);
});
requests = []; // 清空请求队列
isRefreshing = false;
const resData = await http.request(response.config);//恢复请求
return resData;
} else {
// 同时并发出现的请求 新的token没回来之前 先用promise 存入等待队列中
return new Promise((resolve) => {
const fn = () => Promise.resolve(http.request(response.config));
requests.push({
fn,
resolve,
});
});
}
default:
return uni.showToast({
title: "服务器错误",
duration: 1500,
});
}
}
);
export function request(options = {}) {
return http.request(options);
}
export default request;