美文网首页vue合集
axios封装,无感刷新token

axios封装,无感刷新token

作者: Baby_ed6e | 来源:发表于2021-12-08 15:31 被阅读0次

    主要原理

    1、当第一个请求发现token过期,创建一个Promise对象开始请求api刷新token
    2、这个刷新过程中再发现token过期,或者再触发新的接口请求,都是给这个Promise 对象增加then方法(then链)。
    3、相当于收集后面排队的请求,统一等刷新完毕后依次放行!

    业务要求

    1、token过期无感刷新token,没有提示等
    2、页面性能过得去,不能用setTimeout setInterval
    3、可能同时请求不同接口,分别发现token过期,两次触发非常接近,属于并发,第一个发现token过期的接口刚知道,另一个也已经发现了token过期,所以要将它们收集并挂起,在刷新token后自动再次请求一次接口,把第二次接口返回内容返回给第一次请求。这种情况接口会执行两次,一次是发现,一次是刷新后重新执行

    4、如果追求性能细节,那么在刷新的时候,后面触发接口不应该直接请求api,因为就算请求了也是401,不如暂时挂起,等待刷新后再用新的token执行,这种情况只执行一次

    以下是代码实现过程

    实现过程

    1、创建一个刷新token的类

    /**
     * token刷新方法,
     * 1、当第一次出现401的时候,立即从api里请求token,并将“异步体”保存为 refreshPromise
     * 2、后续的请求相当于都是给 apiRefreshToken 添加then
     * 3、当token刷新完毕,refreshPromise 应当清空为下次做准备
     */
    class RefreshToken {
        constructor() {
            this.refreshPromise = undefined;
        }
        apiRefreshToken() {
            if (!this.refreshPromise) {
                this.refreshPromise = new Promise((resolve) => {
                    // 获取到token,存放到本地缓存,此处忽略
                    setTimeout(() => {
                        this.refreshPromise = undefined;
                        resolve();
                    }, 8000);
                });
            }
            return this.refreshPromise;
        }
        refreshIsLoading() {
            return Boolean(this.refreshPromise);
        }
    }
    export default new RefreshToken();
    
    

    2、封装axios

    import axios from "axios";
    import { useRouter } from "vue-router";
    import tokenServer from "./tokenServer";
    import { ElMessage, ElMessageBox } from "element-plus";
    
    // 创建axios实例
    const service = axios.create({
        baseURL: import.meta.env.VITE_BASE_API,
        timeout: 5000,
    });
    
    // 请求头拦截器,组合token
    service.interceptors.request.use(
        (config) => {
            config.headers["Content-Type"] = "application/json;charset=UTF-8";
            config.headers["X-Token"] = "abc"; // 这里在本地缓存拿token
            return config;
        },
        (error) => Promise.reject(error)
    );
    
    // 请求结果拦截器
    service.interceptors.response.use(
        async (response) => {
            const { ret_code, result, ret_msg } = response.data;
            if (ret_code == "200") {
                return Promise.resolve(result);
            } else if (ret_code == 401) {
                // 过期执行刷新token
                // 1、apiRefreshToken 执行刷新并返回正在刷新token的Promise对象, 且同时只有一个。
                // 2、每次执行 apiRefreshToken,相当于给Promise对象添加 then() 链, 所以不必担心多个接口同时发现token过期,
                // 3、等刷新完毕后再次执行api请求。
                try {
                    await tokenServer.apiRefreshToken();
                    return Promise.resolve(service(response.config._original));
                } catch {
                    const router = useRouter();
                    ElMessageBox.alert("登录已失效,请重新登录!", "提示", {
                        confirmButtonText: "去登录",
                        callback: () => {
                            router.push({
                                path: "/login",
                            });
                        },
                    });
                    return Promise.reject();
                }
            } else {
                ElMessage.error(ret_msg);
                return Promise.reject(response.data);
            }
        },
        (error) => {
            ElMessage.error("服务器链接失败,请重试!");
            return Promise.reject(error.response);
        }
    );
    
    // 如果正在刷新token时,需等待token刷新完毕再触发接口请求(暂时挂起)
    // 拓展入参_original,保留原始请求的入参,目的是为了拦击里再次请求接口所需参数
    const request = async (params) => {
        let refreshIsLoading = tokenServer.refreshIsLoading();
        if (refreshIsLoading) {
            await tokenServer.apiRefreshToken();
        }
        params._original = JSON.parse(JSON.stringify(params));
        return service(params);
    };
    
    export default request;
    
    

    相关文章

      网友评论

        本文标题:axios封装,无感刷新token

        本文链接:https://www.haomeiwen.com/subject/histfrtx.html