在uniapp中封装请求接口 (带刷新token)

javascript 复制代码
import { apiUrl } from '@/config/global-config.js'
import { useUserStore } from '../stores'
import { usePageRoute } from "@/composable/usePageRoute.js"

// 默认配置
const DEFAULT_CONFIG = {
	timeout: 60000,
	// showLoading: true,
	// loadingText: '加载中...'
}


// 添加刷新token的方法
const refreshToken = async () => {
	const userStore = useUserStore()
	try {
		const response = await uni.request({
			url: apiUrl + '/auth/refresh',  // 替换成你的刷新token接口
			method: 'POST',
			header: {
				'Content-Type': 'application/json',
				'Authorization': `Bearer ${userStore.token}`
			}
		})

		if (response.data.code === 200) {
			userStore.setToken(response.data.data.token)
			return true
		}
		return false
	} catch (error) {
		return false
	}
}

// 添加一个变量来存储刷新token的Promise
let isRefreshing = false
let refreshSubscribers = []

// 执行等待的请求
const onRefreshed = (token) => {
	refreshSubscribers.forEach(callback => callback(token))
	refreshSubscribers = []
}

// 添加请求到队列
const addSubscriber = (callback) => {
	refreshSubscribers.push(callback)
}


// 请求拦截器
const requestInterceptor = (config) => {
	const userStore = useUserStore()
	const token = userStore.token
	const isMapApi = /apis\.map\.qq\.com/.test(config.url)

	if (token && !isMapApi) {
		config.header.Authorization = `Bearer ${token}`
	}

	// if (config.showLoading) {
	// 	uni.showLoading({ title: config.loadingText })
	// }

	return config
}

// 响应拦截器
const responseInterceptor = async (response) => {
	const userStore = useUserStore()
	// const { code, msg, data } = response.data
	const code = response.data.code
	const msg = response.data.msg
	const data = response.data
	// const data = response.data.data
	// console.log(data,code,msg);



	switch (code) {
		case 200:
			return data
		// return Promise.resolve(data)
		case 401: {
			// 如果是刷新token的请求失败,直接登出
			 if (config.url.includes('/auth/refresh')) {
			 	await handleLogout()
			 	return Promise.reject(new Error('登录已失效'))
			 }

			 // 处理token刷新
			 if (!isRefreshing) {
			 	isRefreshing = true

			 	try {
			 		const refreshResult = await refreshToken()
			 		if (refreshResult) {
			 			const newToken = userStore.token
			 			onRefreshed(newToken)
			 			// 重试当前请求
			 			config.header.Authorization = `Bearer ${newToken}`
			 			return request(config)
			 		} else {
			 			await handleLogout()
			 			return Promise.reject(new Error('登录已失效'))
			 		}
			 	} finally {
			 		isRefreshing = false
			 	}
			 }
			 // 返回一个Promise,将请求暂存
			 return new Promise((resolve) => {
			 	addSubscriber((token) => {
			 		config.header.Authorization = `Bearer ${token}`
			 		resolve(request(config))
			 	})
			 })

			// 上面是刷新token 如果没有刷新token的接口直接登出  把上面的401代码注释 解开下面的两行注释
			//await handleLogout()
			//return Promise.reject(new Error('登录已失效'))

		}
		default:
			uni.showToast({
				icon: 'none',
				title: msg || '请求错误',
				duration: 2000
			})
			return Promise.reject(new Error(msg || '请求错误'))
	}
}

// 添加统一的登出处理方法
const handleLogout = async () => {
	const userStore = useUserStore()
	const pageRoute = usePageRoute()
	const fullPagePath = pageRoute.getCurrentFullPagePath()
	uni.setStorageSync('fullPage', fullPagePath)

	uni.$msgBox('登录已失效,请重新登录', 2000)
	userStore.clearToken()
	userStore.clearUser()
	await uni.$delay(2000)
	uni.reLaunch({ url: '/pages/login/login' })
}

// 统一请求方法
const request = (options = {}) => {
	const config = {
		...DEFAULT_CONFIG,
		...options,
		header: {
			'Content-Type': 'application/json',
			...options.header
		}
	}

	config.url = apiUrl + config.url

	// 应用请求拦截器
	const interceptedConfig = requestInterceptor(config)

	return new Promise((resolve, reject) => {
		uni.request({
			...interceptedConfig,
			success: async (res) => {
				try {
					const result = await responseInterceptor(res)
					resolve(result)
				} catch (error) {
					reject(error)
				}
			},
			fail: (error) => {
				uni.showToast({
					icon: 'none',
					title: '网络错误,请检查网络连接',
					duration: 2000
				})
				reject(error)
			},
			complete: () => {
				// if (config.showLoading) {
				// 	uni.hideLoading()
				// }
			}
		})
	})
}

// 导出请求方法
export const $get = (url, data, options = {}) => {
	return request({
		url,
		data,
		method: 'GET',
		...options
	})
}

export const $post = (url, data, options = {}) => {
	return request({
		url,
		data,
		method: 'POST',
		...options
	})
}

// 挂载到全局
uni.$get = $get
uni.$post = $post
相关推荐
Irene19911 小时前
ElementPlus 与成熟后台框架对比:vue-element-plus-admin、vue-pure-admin等
前端·ui·框架·vue3
尘中客5 小时前
放弃 Echarts?前端直接渲染后端高精度 SVG 矢量图流的踩坑记录
前端·javascript·echarts·前端开发·svg矢量图·echarts避坑
FreeBuf_5 小时前
Chrome 0Day漏洞遭野外利用
前端·chrome
小彭努力中6 小时前
199.Vue3 + OpenLayers 实现:点击 / 拖动地图播放音频
前端·vue.js·音视频·openlayers·animate
2501_916007476 小时前
网站爬虫原理,基于浏览器点击行为还原可接口请求
前端·javascript·爬虫·ios·小程序·uni-app·iphone
前端大波6 小时前
Sentry 每日错误巡检自动化:设计思路与上手实战
前端·自动化·sentry
Highcharts.js7 小时前
适合报表系统的可视化图表|Highcharts支持直接导出PNG和PDF
javascript·数据库·react.js·pdf
ZC跨境爬虫7 小时前
使用Claude Code开发校园交友平台前端UI全记录(含架构、坑点、登录逻辑及算法)
前端·ui·架构
慧一居士7 小时前
Vue项目中,何时使用布局、子组件嵌套、插槽 对应的使用场景,和完整的使用示例
前端·vue.js
叫我一声阿雷吧7 小时前
JS 入门通关手册(35):执行上下文、调用栈与作用域链深度解析
javascript·作用域链·js进阶·执行上下文·调用栈·变量提升·闭包原理