uniapp小程序无感刷新token

  • request.js
javascript 复制代码
// request.js
import {
	getApptoken,
	getStoredApptoken
} from './tokenRequest' // 从合并模块导入

// 全局配置
const MAX_RETRIES = 1 // 最大重试次数
const baseURL = 'https://your-api.com'

// 请求队列和刷新状态
let requestsQueue = []
let isRefreshing = false

// 核心请求函数
export const request = config => {
	return new Promise((resolve, reject) => {
		// 应用请求拦截器
		const processedConfig = requestInterceptor({
			baseURL,
			method: 'POST',
			needRefreshToken: true,
			retryCount: 0, // 记录重试次数
			...config
		})
		// 处理参数拼接(原URL有参数则用&,否则用?)
		const separator = processedConfig.url.includes('?') ? '&' : '?'
		const url = `${processedConfig.baseURL}${processedConfig.url}${separator}token=${processedConfig.apptoken || ''}`
		// 发起请求
		uni.request({
			...processedConfig,
			url,
			success: response => {
				// 应用响应拦截器
				responseInterceptor(response, processedConfig)
					.then(resolvedData => resolve(resolvedData))
					.catch(err => reject(err))
			},
			fail: error => {
				console.error(`请求失败:${processedConfig.url}`, error)
				reject(error)
			}
		})
	})
}

// 请求拦截器:统一处理请求前逻辑
const requestInterceptor = config => {
	config.header = {
		'Content-Type': 'application/json',
		...config.header
	}

	if (config.needRefreshToken) {
		const token = getStoredApptoken() // 使用合并模块的工具函数
		if (token) {
			config.token = token
		}
	}

	return config
}

// 响应拦截器:统一处理响应后逻辑
const responseInterceptor = async (response, config) => {
	const {
		data,
		statusCode
	} = response

	// 处理成功响应
	if (statusCode === 200) {
		return data
	}

	// 处理401/400错误(token过期)
	if ((statusCode === 401 || statusCode === 400) && config.needRefreshToken) {
		// 检查是否超过最大重试次数
		if (config.retryCount >= MAX_RETRIES) {
			uni.showToast({
				title: '重试次数过多,请稍后再试',
				icon: 'none'
			})
			return Promise.reject(new Error('重试次数过多'))
		}

		// 如果正在刷新token,将请求加入队列等待
		if (isRefreshing) {
			return new Promise(resolve => {
				requestsQueue.push(() => resolve(request({
					...config,
					retryCount: config.retryCount + 1
				})))
			})
		}

		// 开始刷新token
		isRefreshing = true

		try {
			// 获取新的token
			const refreshSuccess = await getApptoken()
			if (!refreshSuccess) {
				return Promise.reject(new Error('获取新的token失败'))
			}

			// 重试原请求
			const retryResponse = await request({
				...config,
				retryCount: config.retryCount + 1
			})

			// 执行队列中所有挂起的请求
			requestsQueue.forEach(callback => callback())
			requestsQueue = []

			return retryResponse
		} catch (refreshError) {
			console.error('刷新token出错', refreshError)
			uni.showToast({
				title: '认证失败,请重新登录',
				icon: 'none'
			})
			return Promise.reject(refreshError)
		} finally {
			isRefreshing = false // 重置刷新状态
		}
	}

	// 其他错误处理
	return Promise.reject(new Error(`请求失败,状态码:${statusCode}`))
}

// 封装常用请求方法
export const requestPost = (url, data, config = {}) => {
	return request({
		url,
		data,
		method: 'POST',
		...config
	})
}

export const requestGet = (url, config = {}) => {
	return request({
		url,
		method: 'GET',
		...config
	})
}
  • tokenRequest.js
javascript 复制代码
// tokenRequest.js

// 应用配置
const infoObj = {
	appkey: process.env.ENV_TYPE === 'prod' ? 'C1A1140xxxxxxxxxxxxxxxxxxxxxxxxxxxx' :
		'3232313xxxxxxxxxxxxxxxxxxxxxxxxxxxx',
	appSecret: process.env.ENV_TYPE === 'prod' ? '34D7C4Exxxxxxxxxxxxxxxxxxxxxxxxxxxx' :
		'0A779D4xxxxxxxxxxxxxxxxxxxxxxxxxxxx',
	token: ''
}
const baseURL = 'https://your-api.com'

// 基础请求(无拦截器)
const baseRequest = config => {
	return new Promise((resolve, reject) => {
		uni.request({
			method: 'GET',
			header: { 'Content-Type': 'application/json' },
			dataType: 'json',
			...config,
			success: response => resolve(response.data), // 直接返回data
			fail: error => reject(error)
		})
	})
}

// 获取AppToken
export const getApptoken = () => {
	return new Promise((resolve, reject) => {
		const oldtoken = getStoredApptoken()
		const tokenUrl = `${baseURL}/getToken?token=${oldtoken}`
		baseRequest({ url: tokenUrl })
			.then(res => {
				if (res.token) {
					infoObj.token = res.token
					uni.setStorageSync('infoObj', JSON.stringify(infoObj))
					resolve(true)
				} else {
					uni.showToast({
						title: '更新token出错',
						icon: 'none'
					})
					resolve(false)
				}
			})
			.catch(err => {
				console.error('获取token接口失败', err)
				uni.showToast({
					title: '更新token接口出错',
					icon: 'none'
				})
				resolve(false)
			})
	})
}

// 获取存储的AppToken
export const getStoredApptoken = () => {
	try {
		const stored = uni.getStorageSync('infoObj')
		return stored ? JSON.parse(stored).token : ''
	} catch (error) {
		console.error('获取存储的token失败', error)
		return ''
	}
}
  • xxx.vue
javascript 复制代码
// 示例:调用POST接口(如需发送数据)
const submitData = async () => {
  try {
    const params = { name: 'test', age: 20 };
    // 调用POST接口(自动拼接token到URL)
    const res = await requestPost('/api/submit', params);
    console.log('提交成功', res);
  } catch (err) {
    console.error('提交失败', err);
  }
};
相关推荐
中微子1 小时前
React 状态管理 源码深度解析
前端·react.js
加减法原则2 小时前
Vue3 组合式函数:让你的代码复用如丝般顺滑
前端·vue.js
yanlele2 小时前
我用爬虫抓取了 25 年 6 月掘金热门面试文章
前端·javascript·面试
lichenyang4532 小时前
React移动端开发项目优化
前端·react.js·前端框架
你的人类朋友3 小时前
🍃Kubernetes(k8s)核心概念一览
前端·后端·自动化运维
web_Hsir3 小时前
vue3.2 前端动态分页算法
前端·算法
烛阴3 小时前
WebSocket实时通信入门到实践
前端·javascript
草巾冒小子3 小时前
vue3实战:.ts文件中的interface定义与抛出、其他文件的调用方式
前端·javascript·vue.js
DoraBigHead4 小时前
你写前端按钮,他们扛服务器压力:搞懂后端那些“黑话”!
前端·javascript·架构
Xiaouuuuua4 小时前
一个简单的脚本,让pdf开启夜间模式
java·前端·pdf