uniapp无感刷新token实现过程

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

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

javascript 复制代码
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)
	})

业务侧列表页请求接口:(我的问题就在于调用接口后,下方的代码不执行,导致页面数据不更新
也就是说该语句没有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即可解决啦!
相关推荐
zqx_714 分钟前
随记 前端框架React的初步认识
前端·react.js·前端框架
惜.己31 分钟前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5
什么鬼昵称1 小时前
Pikachu-csrf-CSRF(get)
前端·csrf
长天一色1 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript
NiNg_1_2341 小时前
npm、yarn、pnpm之间的区别
前端·npm·node.js
秋殇与星河2 小时前
CSS总结
前端·css
NiNg_1_2342 小时前
Vue3 Pinia持久化存储
开发语言·javascript·ecmascript
读心悦2 小时前
如何在 Axios 中封装事件中心EventEmitter
javascript·http
BigYe程普2 小时前
我开发了一个出海全栈SaaS工具,还写了一套全栈开发教程
开发语言·前端·chrome·chatgpt·reactjs·个人开发
神之王楠2 小时前
如何通过js加载css和html
javascript·css·html