首页 > 其他分享 >ts + axios token无感刷新,及重新请求后页面不刷新问题

ts + axios token无感刷新,及重新请求后页面不刷新问题

时间:2023-11-09 11:12:20浏览次数:32  
标签:axios return error token 刷新 const message data 无感

最近上班遇到的新需求,token无感刷新,参考了很多博客,也看了渡一老师的视频,功能是实现了,但是发现重新请求后页面数据没有更新
遇到相同问题的先理清代码执行顺序和Promise,看看执行结果有没有resolve()出去。
话不多说,直接上代码,因为自己封装的请求和大家的不一样,仅供参考
无感刷新token在请求拦截这里

import type {
    AxiosInstance,
    AxiosRequestConfig,
    AxiosResponse,
    InternalAxiosRequestConfig,
    RawAxiosResponseHeaders
} from 'axios'
import axios from "axios";
import {ElMessage} from "element-plus";
import type {responseDataType} from "@/types/common";
import {ElLoading} from 'element-plus'
import {refreshToken} from "@/api/system/role";
import {logout} from "@/api/login";

const BASE_URL = import.meta.env.VITE_API_BASE_URL

class Request {
    baseConfig: AxiosRequestConfig = {
        baseURL: BASE_URL,
        timeout: 60000
    }
    // 是否正在刷新中
    isRefreshToken = false
    // 需要忽略的提示。忽略后,自动 Promise.reject('error')
    ignoreMsg = [
        '无效的刷新令牌', // 刷新令牌被删除时,不用提示
        '刷新令牌已过期' // 使用刷新令牌,刷新获取新的访问令牌时,结果因为过期失败,此时需要忽略。否则,会导致继续 401,无法跳转到登出界面
    ]
    // 请求队列
    requestList: any[] = []
    header: RawAxiosResponseHeaders | null = null

    private async handleLogout() {
        const {data} = await logout()
        if (data) {
            setTimeout(function () {
                localStorage.clear()
                sessionStorage.clear()
                location.href = '/'
            }, 1000)
        }
    }

    private httpRequest<T>(config: AxiosRequestConfig): Promise<T> {
        const instance: AxiosInstance = axios.create()
        //请求拦截
        instance.interceptors.request.use((config: InternalAxiosRequestConfig) => {
            const token = localStorage.getItem("token")
            if (config.data instanceof FormData) {
                config.headers['Content-Type'] = 'multipart/form-data'
            } else {
                config.headers['Content-Type'] = 'application/json;charset=utf-8';
            }
            if (token) {
                config.headers!.Authorization = `Bearer ${token}`;
            }
            return config
        }, (error: any) => {
            return Promise.reject(error);
        })
        //响应拦截、无感刷新在这
        instance.interceptors.response.use((response: AxiosResponse) => {
            // 未设置状态码则默认成功状态
            const code = response.data.code || 0
            // 获取错误信息
            const msg = response.data.msg
            return new Promise(async (resolve, reject) => {
                if (code === 0) {
                    this.header = response.headers
                    resolve(response.data)
                } else {
                    if (code === 401) {
                        if (!this.isRefreshToken) {
                            this.isRefreshToken = true
                            const token = localStorage.getItem('refresh_token')
                            if (token === null) {
                                ElMessage.error('认证失败,请重新登录')
                                return this.handleLogout()
                            } else {
                                try {
                                    const {code, data} = await refreshToken(token)
                                    if (code !== 0) return this.handleLogout()
                                    // 刷新成功,更新 token 并重新发送请求
                                    localStorage.setItem('token', data.accessToken);
                                    localStorage.setItem('refresh_token', data.refreshToken)
                                    const newConfig = {...response.config};
                                    newConfig.headers.Authorization = `Bearer ${data.accessToken}`;
                                    this.requestList.forEach((cb: any) => {
                                        cb()
                                    })
                                    this.requestList = []
                                    return instance(newConfig)
                                } catch (e) {
                                    // 2.2 刷新失败,只回放队列的请求
                                    this.requestList.forEach((cb: any) => {
                                        cb()
                                    })
                                    // 提示是否要登出。即不回放当前请求!不然会形成递归
                                    return this.handleLogout()
                                } finally {
                                    this.requestList = []
                                    this.isRefreshToken = false
                                }
                            }
                        } else {
                  // 添加到队列,等待刷新获取到新的令牌
return this.requestList.push(() => {
                                const newConfig = {...response.config};
                                newConfig.headers.Authorization = `Bearer ${localStorage.getItem('token')}`;
                                resolve(instance(newConfig));
                            });
                        }
                    } else if (code === 1003005001) {
                        ElMessage.error('学生导入失败')
                        reject(response.data)
                    } else if (this.ignoreMsg.indexOf(msg) !== -1) {
                        // 如果是忽略的错误码,直接返回 msg 异常
                        return Promise.reject(msg)
                    } else {
                        ElMessage.error(msg)
                        reject(response.data)
                    }

                }
            })
        }, error => {
            let {message} = error
            if (message === 'Network Error') {
                message = '请求异常,请联系系统管理员或请稍后重试'
            } else if (message.includes('Unable to find')) {
                message = '服务未找到,请稍后重试'
            } else if (message.includes('code 404')) {
                message = '请求接口不存在,请联系管理员'
            } else if (message.includes('timeout')) {
                message = '系统接口请求超时,请稍后重试'
            } else if (message.includes('Request failed with status code')) {
                message = error.response.data.message
            }
            ElMessage.error(message)
            return Promise.reject(error)
        })
        return instance(Object.assign({}, config, this.baseConfig))
    }

    public get<T>(url: string, options?: any): Promise<responseDataType<T>> {
        return this.httpRequest<responseDataType<T>>({
            url: url,
            method: 'GET',
            params: options
        })
    }

    public post<T>(url: string, options?: any): Promise<responseDataType<T>> {
        return this.httpRequest<responseDataType<T>>({
            url: url,
            method: 'POST',
            data: options
        })
    }

    public put<T>(url: string, options?: any): Promise<responseDataType<T>> {
        return this.httpRequest<responseDataType<T>>({
            url: url,
            method: 'PUT',
            ...(typeof options === "string" ? {data: options} : {params: options})
        })
    }

    public delete<T>(url: string, options?: any): Promise<responseDataType<T>> {
        return this.httpRequest<responseDataType<T>>({
            url: url,
            method: 'DELETE',
            ...(typeof options === 'string' ? {data: options} : {params: options})
        })
    }

    /**
     *@Date:2023-09-21 20:56:52
     *@description:下载文件的公共方法
     *@param{*} fileUrl 下载路径
     *@param{*} methods 请求方法
     *@param{*} params 请求参数
     *@param{*} fileName 文件名
     */
    public download(fileUrl: string, methods?: string, params?: any, fileName?: string) {
        const loading = ElLoading.service({
            lock: true,
            text: '文件打包中,请稍候...',
        })
        this.httpRequest<Blob>({
            method: methods || 'get',
            url: fileUrl,
            responseType: 'blob', // 设置响应数据类型为二进制对象
            ...(methods === 'get' ? {params: params} : {data: params})
        }).then((response: Blob) => {
            if (response.size <= 0) {
                loading.close()
                return ElMessage.error('无可下载数据')
            }
            // 处理下载的二进制文件流
            const blob = response;

            // 创建一个虚拟的 <a> 元素用于触发下载
            const a = document.createElement('a');
            a.href = window.URL.createObjectURL(blob);

            // 指定文件名并设置下载属性
            if (fileName) {
                a.setAttribute('download', fileName)
            }

            // 模拟点击事件触发下载
            const clickEvent = new MouseEvent('click', {
                view: window,
                bubbles: true,
                cancelable: false,
            });
            a.dispatchEvent(clickEvent);
            loading.close()
            // 下载成功后解析 Promise

        })
            .catch((error) => {
                console.error('下载文件失败:', error);
                loading.close()
            });
    }
}

export const hrRequest = new Request()

 

标签:axios,return,error,token,刷新,const,message,data,无感
From: https://www.cnblogs.com/cstd/p/17819260.html

相关文章

  • 【Cpp 基础】主动刷新 cout 缓存区
    使用额外的“刷新”功能(<<flush)来确保根据我们的要求显示输出。//C++程序演示flush函数的使用#include<iostream>#include<thread>#include<chrono>usingnamespacestd;intmain(){ for(inti=1;i<=5;++i) { cout<<i<<""<&......
  • vue3 axios 获得基地址
    1.位置 //axios基础的封装importaxiosfrom'axios'import'element-plus/es/components/message/style/css'import{ElMessage}from'element-plus'consthttpInstance=axios.create({baseURL:'http://laravel.cn',//基......
  • JS脚本实现刷新页面,随机加载背景图片
    新建switch.js,内容如下: varimgs=[ "https://mlabs.gitee.io/pics/webp/tiankong002-mid.webp", "https://mlabs.gitee.io/pics/webp/wallhaven-gp1q87.webp", "https://mlabs.gitee.io/pics/webp/shanshui007sm......
  • uniapp实用功能代码(小程序支付,图片保存,返回刷新,分享到朋友圈)
    1.uniapp小程序支付:uni.request({url:"http://xxxxxx/payOrder",//后端接口返回调起支付需要的参数data:{userId:1,//此接口需要的参数一般有多个此仅为示例},method:"POST",success:(res)=>{console.log(res.data,"这......
  • 项目中难点-页面点击“取消”按钮实现无感刷新--reload
    1、在App.vue页面中注册provide中定义reloadprovide(){return{reload:this.reload}} 2、在App.vue页面中的methods中定义方法reload目的通过控制router-view的显示与隐藏进行重新加载页面,实现无感刷新。reload(){this.isRouterAlive=falsethis.$next......
  • 如何在不刷新页面的情况下更改Nuxt.js路由器的路由参数?
    要在不刷新页面的情况下更改Nuxt.js路由器的路由参数,你可以使用Nuxt.js提供的$router.push方法,并传递一个新的路由对象。下面是一个示例://使用$router.push更新路由参数this.$router.push({path:'/your-route-path',query:{param1:'value1',param2:'value2'......
  • 如何在Vue.js中添加headers(标头) 使用 axios,单独请求,所有请求 添加请求头
    如何在Vue.js中添加headers(标头)使用axios,单独请求,所有请求添加请求头Vue.js是一个流行的前端框架,它以其简单易用的API和高度可组合的架构而闻名。当你构建一个Web应用时,你通常会使用一个HTTP客户端来与API交互。该客户端可以是一个浏览器内部的XMLHttpRequest,也可以......
  • 基于 Axios 封装一个完美的双 token 无感刷新
    用户登录之后,会返回一个用户的标识,之后带上这个标识请求别的接口,就能识别出该用户。标识登录状态的方案有两种:session和jwt。session是通过cookie返回一个id,关联服务端内存里保存的session对象,请求时服务端取出cookie里id对应的session对象,就可以拿到用户信息。jwt......
  • 自定义简单的axios方法
    functionmyAxios(config){returnnewPromise((resolve,reject)=>{constxhr=newXMLHttpRequest()//如果存在想要放在链接后的参数?name=1&password=2if(config.params){constparamsObj=newURLSearchParams(conf......
  • 使用async和await获取axios的数据注意事项
    使用async和await获取axios的数据的注意事项确定正确使用asyncfunctiongetInfo(){constres=awaitaxios.get('http://example.com')returnres.data}上述代码等同于asyncfunctiongetInfo(){constresult=(awaitaxios.get('http://example.com')).data......