• uniapp无感刷新token实现过程


    路漫漫其修远兮,前端道路逐渐迷茫,时隔好久好久终于想起了我还有一个小博客,最近在一直在弄uniapp,属实有被恶心到,但也至少会用了,最近实现了一个比较通用的功能,就是无感刷新token,但在过程中发现一个问题就是token刷新了,接口也重新请求了,但页面数据就是没有刷新

    上面问题归根结底还是自己对于promise、async、await等知识理解不够深刻导致的,先看看无感刷新token怎么实现的吧!废话不多说上代码(代码都有注释):

    import Request from './request' // 对uniapp.request的简易封装
    import { getRefreshToken } from '@/common/api/apis.js' // 刷新token接口
    const http = new Request()
    
    let isRefresh = false // 是否处于刷新token状态中
    let fetchApis = [] // 失效后同时发送请求的容器
    let refreshCount = 0 // 限制无感刷新的最大次数
    function onFetch(newToken) {
    	refreshCount += 1
    	if (refreshCount === 3) {
    		refreshCount = 0
    		fetchApis = []
    		return Promise.reject()
    	}
    	fetchApis.forEach(callback => {
    		callback(newToken)
    	})
    	// 清空缓存接口
    	fetchApis = []
    	return Promise.resolve()
    }
    
    // 响应拦截(主要看error,因为token过期一般你们后端接口都是返回401/402,在对应case下处理刷新token逻辑即可)
    http.interceptor.response((response) => {
    	/* 请求之后拦截器 */
        if (response.config.loading) {
          uni.hideLoading()
        }
        // 请求成功但接口返回的错误处理
        if (response.data.statusCode && +response.data.statusCode !== 200) {
          if (!response.config.needPromise) {
            console.log('error', response)
            uni.showModal({
              title: '提示',
              content: response.data.message,
              showCancel: false,
              confirmText: '知道了'
            })
            // 中断
            return new Promise(() => {})
          } else {
            // reject Promise
            return Promise.reject(response.data)
          }
        }
        return response
    }, (error) => {
    	// 请求错误做点什么
    	const token = uni.getStorageSync('token')
        const refreshToken = uni.getStorageSync('refreshToken')
        // DESC: 不需要做无感刷新的白名单接口
        const whiteFetchApi = ['/dealersystem/jwtLogin', '/dealersystem/smsLogin', '/sso2/login', '/dealersystem/isLogin']
        switch (error.statusCode) {
    		// token过期处理,实现无感刷新
    	  	case 401:
    	  	case 402:
    		  	// 如果登录过,token过期走refreshToken逻辑
    		    if (token && !whiteFetchApi.includes(error.config.url)) {
    				if (!isRefreshing) {
    					isRefreshing = true
    					getRefreshToken({ refreshToken }).then(res => {
    						let newToken = res.data
    						onTokenFetched(newToken).then(res => {}).catch(err => {
    							// 超过循环次数时,回到登录页,这里可以添加你执行退出登录的逻辑
    							uni.showToast({ title: '登录失效,请重新登录', icon: 'error' })
    							setTimeout(() => {
    								uni.reLaunch({url: '/pages/login/login'})
    							}, 1500)
    						})
    					}).catch(err => {
    						// refreshToken接口报错,证明refreshToken也过期了,那没办法啦重新登录呗
    						uni.showToast({ title: '登录失效,请重新登录', icon: 'error' })
    			            setTimeout(() => {
    			              uni.reLaunch({ url: '/pages/login/login' })
    			            }, 1500)
    					}).finally(() => { isRefreshing = false })
    				}
    				return new Promise((resolve) => { // 此处的promise很关键,就是确保你的接口返回值在此处resolve,以便
    		        	addFetchApi((newToken) => {
    		         	 	error.config.header.token = newToken
    		          		resolve(http.request(error.config))
    		        	})
    		      	})
    			} else {
    				// 没有登录过,直接输入链接访问的情况
    				if (whiteFetchApi.includes(error.config.url)) {
    					return Promise.reject(error.data)
    				}
    		      	uni.showToast({ title: '您未登录,请前往登录', icon: 'error' })
    			    setTimeout(() => {
    		        	uni.reLaunch({ url: '/pages/login/login' })
    		      	}, 1500)
    		      	return Promise.reject(error.data)
    			}
    		default:
    		    errorMsg = error.data.message || ''
    		    break
    		}
    		uni.showModal({
    		  title: '提示',
    		  content: errorMsg,
    		  showCancel: false,
    		  confirmText: '知道了'
    		})
        	return Promise.reject(error.data)
    	})
    
    • 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

    业务侧列表页请求接口:(我的问题就在于调用接口后,下方的代码不执行,导致页面数据不更新
    也就是说该语句没有resolve回结果在这里插入图片描述
    在这里插入图片描述

    那么,怎么排查问题呢,首先先复现问题:
    1、token我在localStorage缓存了一份,可以在谷歌浏览器中更改token,模拟token过期
    在这里插入图片描述
    2、触发如上项目列表的分页接口:
    在这里插入图片描述
    可以看到已经触发了刷新token接口,并且重新请求上次失败的列表接口,无感刷新token逻辑已经正常,但为什么列表接口请求成功,还一直loading没关闭捏,就是因为后续代码未执行,await接口的返回没有成功resolve

    3、排查await的列表接口为何没有成功resolve
    (1) 列表页
    在这里插入图片描述
    (2) 接口配置页
    在这里插入图片描述
    (3) 接口全局配置文件api/index.js,就是在此处将接口返回数据resolve回来,此时再看http.request方法
    在这里插入图片描述
    (4) uni.request封装文件request.js,发现问题所在,当接口请求失败时,这里直接reject了,那么真相大白;
    在这里插入图片描述

    熟悉promise的话,应该知道:

    • resolve一个promise相当于返回一个新的promise
    • await语句要成功获取到值时,该promise必须是resolve的fulfilled状态,如若未对await语句进行try catch处理,那么会阻止后续代码的执行
      在这里插入图片描述
      所以怎么改呢,将上述request.js文件的reject替换成resolve即可解决啦!
  • 相关阅读:
    矩阵论复习提纲
    【DB】Windows 环境修改MySql 8.0.x 密码
    OPTEE:TA和TA加载(一)
    SpringBoot文件上传
    uniapp app获取keystore等一系列常用数据
    《lwip学习6》-- ARP协议
    面试题:Java中为什么只有值传递?
    图论-图的深度优先遍历-Java
    DAY5-深度学习100例-卷积神经网络(CNN)天气识别
    【C++】优先级队列priority_queue模拟实现&&仿函数
  • 原文地址:https://blog.csdn.net/weixin_40618068/article/details/133920963