本文将详细介绍一个无感刷新 Token 的封装请求方法,该方法主要用于在用户未察觉的情况下自动刷新 Token,以保证用户在使用过程中不会因为 Token 过期而出现登录状态丢失的情况。下面将对该方法的功能点和逻辑进行详细解析,并添加必要的注释以便更好地理解其工作原理。
刷新 Token 的方法
此方法用于刷新 Token。当检测到当前 Token 已过期时,会调用此方法自动刷新 Token。
// 刷新Token的方法
function refreshToken() {
return new Promise((resolve, reject) => {
// 调用实际的刷新Token接口
uni.request({
url: baseURL + '/app-api/member/auth/refresh-token',
method: 'POST',
header: {
"tenant-id": "1"
},
data: {
'refreshToken': uni.getStorageSync('refreshToken')
},
success: (res) => {
if (res.data.code === 0) {
// 更新本地存储中的 Token 和 refreshToken
uni.setStorageSync("token", res.data.data.accessToken);
uni.setStorageSync("refreshToken", res.data.data.refreshToken);
// 返回新的 accessToken
resolve(res.data.data.accessToken);
} else {
// 刷新 Token 失败
reject(new Error('Refresh token failed'));
}
},
fail: reject
});
});
}
三、封装的请求方法
此方法是整个无感刷新 Token 的核心,它负责发送网络请求并处理 Token 过期的情况。
let isRefreshing = false; // 标记是否正在刷新 Token
let failedQueue = []; // 存储因 Token 过期而失败的请求
const myRequest = function(options) {
return new Promise((resolve, reject) => {
const token = uni.getStorageSync('token');
var merged = options.data;
const send = () => {
uni.request({
url: baseURL + options.url,
data: merged,
header: {
"tenant-id": "1",
"Authorization": "Bearer " + uni.getStorageSync('token')
},
method: options.method,
success: (res) => {
switch (res.data.code) {
case 0:
case '0':
// 请求成功
resolve(res.data.data);
break;
case 410000:
case 410001:
case 410002:
case 401:
// Token 过期
if (!isRefreshing) {
isRefreshing = true;
// 开始刷新 Token
refreshToken().then(newToken => {
// 更新本地存储中的 Token
uni.setStorageSync('token', newToken);
isRefreshing = false;
// 重新发送之前失败的请求
failedQueue.forEach(cb => cb());
failedQueue = [];
}).catch(() => {
isRefreshing = false;
// Token 刷新失败,跳转到登录页面
uni.reLaunch({
url: '/pages/login/login'
});
});
}
// 将当前请求加入重试队列
failedQueue.push(() => send());
break;
default:
// 其他错误
reject(res.data);
if (!options.hidetoast) {
uni.showToast({
title: res.data.msg || res.data.message || '系统未知错误',
duration: 2000,
icon: 'none'
});
}
break;
}
},
fail: (err) => {
uni.showToast({
title: '请求失败',
duration: 2000,
icon: 'none'
});
reject(err);
}
});
};
// 发送请求
send();
});
};
module.exports = {
myRequest
};
四、关键逻辑解析
- Token 刷新逻辑:
- 当检测到 Token 过期时 (
case 410000
,case 410001
,case 410002
,case 401
),会触发 Token 刷新流程。 - 如果当前没有正在进行的 Token 刷新操作 (
!isRefreshing
),则开始刷新 Token。 - 刷新 Token 成功后,更新本地存储中的 Token,并重新发送之前因 Token 过期而失败的请求。
- 重试队列管理:
- 使用
failedQueue
数组来存储因 Token 过期而失败的请求。 - 当 Token 刷新成功后,遍历
failedQueue
并重新发送这些请求。
- 错误处理:
- 如果刷新 Token 失败,会跳转到登录页面。
- 对于其他类型的错误,会显示错误提示。
五、总结
通过以上封装的请求方法,我们可以实现在用户未察觉的情况下自动刷新 Token,从而避免了因 Token 过期而导致的登录状态丢失问题。这种方法不仅提高了用户体验,也简化了前端开发人员对 Token 过期问题的处理。希望本文能帮助你更好地理解和使用无感刷新 Token 的封装请求方法。
import {baseURL} from './base.js'
// 刷新Token的方法
function refreshToken() {
return new Promise((resolve, reject) => {
// 这里调用实际的刷新Token接口
uni.request({
url: baseURL + '/app-api/member/auth/refresh-token',
method: 'POST',
header: {
"tenant-id": "1"
},
data: {
'refreshToken': uni.getStorageSync('refreshToken')
},
success: (res) => {
if (res.data.code === 0) {
uni.setStorageSync("token", res.data.data.accessToken)
// uni.setStorageSync("openid", res.data.data.openid)
uni.setStorageSync("refreshToken", res.data.data.refreshToken)
uni.setStorageSync("userId", res.data.data.userId)
resolve(res.data.data.accessToken);
} else {
reject(new Error('Refresh token failed'));
}
},
fail: reject
});
});
}
let isRefreshing = false;
let failedQueue = [];
const myRequest = function(options) {
return new Promise((resolve, reject) => {
const token = uni.getStorageSync('token');
var merged = options.data;
const send = () => {
uni.request({
url: baseURL + options.url,
data: merged,
header:{
"tenant-id": "1",
"Authorization": "Bearer " +uni.getStorageSync('token')
},
method: options.method,
success: (res) => {
switch (res.data.code) {
case 0:
case '0':
resolve(res.data.data);
break;
case 410000:
case 410001:
case 410002:
case 401:
if (!isRefreshing) {
isRefreshing = true;
refreshToken().then(newToken => {
uni.setStorageSync('token', newToken);
isRefreshing = false;
// 重新发送之前失败的请求
failedQueue.forEach(cb => cb());
failedQueue = [];
}).catch(() => {
isRefreshing = false;
uni.reLaunch({
url: '/pages/login/login'
});
});
}
failedQueue.push(() => send());
break;
default:
reject(res.data);
if (!options.hidetoast) {
uni.showToast({
title: res.data.msg || res.data.message || '系统未知错误',
duration: 2000,
icon: 'none'
});
}
break;
}
},
fail: (err) => {
uni.showToast({
title: '请求失败',
duration: 2000,
icon: 'none'
});
reject(err);
}
});
};
send();
});
};
module.exports = {
myRequest
};