本文将详细介绍一个无感刷新 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
});
});
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
三、封装的请求方法
此方法是整个无感刷新 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
};
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
四、关键逻辑解析
- 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
};
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.