本文将详细介绍一个无感刷新 Token 的封装请求方法,该方法主要用于在用户未察觉的情况下自动刷新 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.

四、关键逻辑解析

  1. Token 刷新逻辑
  • 当检测到 Token 过期时 (case 410000case 410001case 410002case 401),会触发 Token 刷新流程。
  • 如果当前没有正在进行的 Token 刷新操作 (!isRefreshing),则开始刷新 Token。
  • 刷新 Token 成功后,更新本地存储中的 Token,并重新发送之前因 Token 过期而失败的请求。
  1. 重试队列管理
  • 使用 failedQueue 数组来存储因 Token 过期而失败的请求。
  • 当 Token 刷新成功后,遍历 failedQueue 并重新发送这些请求。
  1. 错误处理
  • 如果刷新 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.