本文将详细介绍一个无感刷新 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
        });
    });
}

三、封装的请求方法

此方法是整个无感刷新 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. 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
};