uni-app三部曲之二: 封装http请求

1.引言

前面一篇文章写了使用Pinia进行全局状态管理。

这篇文章主要介绍一下封装http请求,发送数据请求到服务端进行数据的获取。

感谢:

1.yudao-mall-uniapp: 芋道商城,基于 Vue + Uniapp 实现,支持分销、拼团、砍价、秒杀、优惠券、积分、会员等级、小程序直播、页面 DIY 等功能,100% 开源

2.3.x文档 | luch-request

3.Day1-01-uni-app小兔鲜儿导学视频_哔哩哔哩_bilibili

2.token过期后的重新获取思路

在进行登录后,通过本地缓存,存储获取到的accessToken与refreshToken,accessToken的过期时间为30分钟,refreshToken过期时间为30天。在每次发送请求时,通过http的请求拦截器,放入accessToken进入header中,后端进行校验,当accessToken过期后,后端返回的封装中,code为401,此时应该用refreshToken无感知刷新accessToken继续本次的请求,当refreshToken也过期后,就需要用户重新进行登录。

3.代码

代码主要介绍三个部分,第一部分是自定义http的请求拦截器与响应拦截器,第二部分是封装http的请求,第三部分是如何发送具体的请求。

1.自定义拦截器

请求拦截器主要定义发送请求时的参数,响应拦截器主要处理返回时各种情况。具体可查看文档

import { getRefreshToken, getAccessToken, setAccessToken } from '@/utils/auth'
import { platform } from '@/utils/platform'
import { useUserStore } from '@/store'
import Request from 'luch-request'
import * as authApi from '@/api/auth'const options = {// 显示操作成功消息 默认不显示showSuccess: false,// 成功提醒 默认使用后端返回值successMsg: '',// 显示失败消息 默认显示showError: true,// 失败提醒 默认使用后端返回信息errorMsg: '',// 显示请求时loading模态框 默认显示showLoading: true,// loading提醒文字loadingMsg: '加载中',// 需要授权才能请求 默认放开auth: false,// ...
}// Loading全局实例
const LoadingInstance = {target: null,count: 0,
}/*** 关闭loading*/
function closeLoading() {if (LoadingInstance.count > 0) LoadingInstance.count--if (LoadingInstance.count === 0) uni.hideLoading()
}
/*** @description 请求基础配置 可直接使用访问自定义请求*/
const http = new Request({// 请求基准地址baseURL: import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL,timeout: 8000,header: {Accept: '*/*','Content-Type': 'application/json;charset=UTF-8',platform,},// #ifdef APP-PLUSsslVerify: false,// #endif// #ifdef H5// 跨域请求时是否携带凭证(cookies)仅H5支持(HBuilderX 2.6.15+)withCredentials: false,// #endifcustom: options,
})/*** @description 请求拦截器*/
http.interceptors.request.use((config) => {// 自定义处理【loading 加载中】:如果需要显示 loading,则显示 loadingif (config.custom.showLoading) {LoadingInstance.count++LoadingInstance.count === 1 &&uni.showLoading({title: config.custom.loadingMsg,mask: true,fail: () => {uni.hideLoading()},})}// 添加 token 请求头标识const token = getAccessToken()if (token) {config.header.Authorization = `Bearer ${token}`}return config},(error) => {return Promise.reject(error)},
)/*** @description 响应拦截器*/
http.interceptors.response.use((response) => {// 自定处理【loading 加载中】:如果需要显示 loading,则关闭 loadingresponse.config.custom.showLoading && closeLoading()// 返回结果:包括 code + data + msgconst resData = response.dataconst code = resData.codeif (code === 200) {return Promise.resolve(response.data)} else if (code === 401) {return refreshToken(response.config)} else {uni.showToast({title: resData.message || '出错啦!',icon: 'none',mask: true,})}},(error) => {let errorMessage = '网络请求出错'if (error !== undefined) {switch (error.statusCode) {case 400:errorMessage = '请求错误'breakcase 401:errorMessage = '请登录'// 正常情况下,后端不会返回 401 错误,所以这里不处理 handleAuthorizedbreakcase 403:errorMessage = '拒绝访问'breakcase 404:errorMessage = '请求出错'breakcase 408:errorMessage = '请求超时'breakcase 429:errorMessage = '请求频繁, 请稍后再访问'breakcase 500:errorMessage = '服务器开小差啦,请稍后再试~'breakcase 501:errorMessage = '服务未实现'breakcase 502:errorMessage = '网络错误'breakcase 503:errorMessage = '服务不可用'breakcase 504:errorMessage = '网络超时'breakcase 505:errorMessage = 'HTTP 版本不受支持'break}if (error.errMsg.includes('timeout')) errorMessage = '请求超时'// #ifdef H5if (error.errMsg.includes('Network'))errorMessage = window.navigator.onLine ? '服务器异常' : '请检查您的网络连接'// #endif}if (error && error.config) {if (error.config.custom.showError === false) {uni.showToast({title: error.data?.msg || errorMessage,icon: 'none',mask: true,})}error.config.custom.showLoading && closeLoading()}return false},
)// Axios 无感知刷新令牌,参考 https://www.dashingdog.cn/article/11 与 https://segmentfault.com/a/1190000020210980 实现
let requestList = [] // 请求队列
let isRefreshToken = false // 是否正在刷新中
const refreshToken = async (config) => {// 如果当前已经是 refresh-token 的 URL 地址,并且还是 401 错误,说明是刷新令牌失败了,直接返回 Promise.reject(error)if (config.url.indexOf('/auth/refresh-token') >= 0) {isRefreshToken = falseuni.navigateTo({ url: '/pages/login/index' })return Promise.reject(new Error('error'))}console.log('过期', config)// 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了if (!isRefreshToken) {isRefreshToken = true// 1. 如果获取不到刷新令牌,则只能执行登出操作const refreshToken = getRefreshToken()if (!refreshToken) {return handleAuthorized()}// 2. 进行刷新访问令牌const refreshTokenData = reactive({refreshToken: getRefreshToken(),clientId: import.meta.env.VITE_CLIENT_ID,})const res = await authApi.refreshToken(refreshTokenData)console.log(res)setAccessToken(res.data.accessToken)try {// 2.1 刷新成功,则回放队列的请求 + 当前请求config.header.Authorization = 'Bearer ' + getAccessToken()requestList.forEach((cb) => {cb()})requestList = []return request(options)} catch (e) {// 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。// 2.2 刷新失败,只回放队列的请求requestList.forEach((cb) => {cb()})// 提示是否要登出。即不回放当前请求!不然会形成递归return handleAuthorized()} finally {requestList = []isRefreshToken = false}} else {// 添加到队列,等待刷新获取到新的令牌return new Promise((resolve) => {console.log('重试', config)requestList.push(() => {config.header.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改resolve(request(options))})})}
}/*** 处理 401 未登录的错误*/
const handleAuthorized = () => {const userStore = useUserStore()userStore.userLogout()isRefreshToken = false// 是否进入登录页uni.showModal({title: '提示',content: '重新登录?',success: function (res) {if (res.confirm) {uni.navigateTo({ url: '/pages/login/index' })}},})// 登录超时return new Promise<IResData<boolean>>((resolve, reject) => {const res: IResData<boolean> = {code: 401,message: '请重新登录',data: false,}reject(res)})
}const request = (config) => {return http.middleware(config)
}export default request
auth.ts/*** 存储用户身份信息令牌*/export const CACHE_KEY = {ACCESS_TOKEN: 'access_token',REFRESH_TOKEN: 'refresh_token',
}// 存储访问令牌
export const setAccessToken = (accessToken: string) => {uni.setStorageSync(CACHE_KEY.ACCESS_TOKEN, accessToken)
}// 存储刷新令牌
export const setRefreshToken = (refreshToken: string) => {uni.setStorageSync(CACHE_KEY.REFRESH_TOKEN, refreshToken)
}// 获取访问令牌
export const getAccessToken = () => {return uni.getStorageSync(CACHE_KEY.ACCESS_TOKEN)
}// 获取刷新令牌
export const getRefreshToken = () => {return uni.getStorageSync(CACHE_KEY.REFRESH_TOKEN)
}// 清理本地所有缓存
export const clearStorage = () => {uni.clearStorageSync()
}

2.封装http请求

/*** 封装不同类型的restful请求*/import request from './request'// 全局要用的类型放到这里type IResData<T> = {code: numbermessage: stringdata: T
}export default {get: async <T = any>(options: any) => {const res = await request({ method: 'GET', ...options })return res as unknown as IResData<T>},post: async <T = any>(option: any) => {const res = await request({ method: 'POST', ...option })return res as unknown as IResData<T>},postOriginal: async (option: any) => {const res = await request({ method: 'POST', ...option })return res},delete: async <T = any>(option: any) => {const res = await request({ method: 'DELETE', ...option })return res as unknown as IResData<T>},put: async <T = any>(option: any) => {const res = await request({ method: 'PUT', ...option })return res as unknown as IResData<T>},download: async <T = any>(option: any) => {const res = await request({ method: 'GET', responseType: 'blob', ...option })return res as unknown as Promise<T>},upload: async <T = any>(option: any) => {option.headersType = 'multipart/form-data'const res = await request({ method: 'POST', ...option })return res as unknown as Promise<T>},
}

3.定义请求

import http from '@/service/http'/** 用户登录 */
export const login = (data: LoginReqVO) => {return http.post({ url: '/auth/login', data })
}

4.写在最后

在本项目开始,使用了uni.request来发送http请求,通过uni-app的拦截器配置请求拦截器,后面学习研究的时候发现了luch-request,通过文档然后参考yudao-mall-uniapp项目,封装http请求,通过测试,发现能满足实际需用需求。

当然,本篇文章写的比较简陋,水平有限,欢迎共同探讨指教。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/44178.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电脑自动重启是什么原因呢?99%人都不知道的解决办法,直接打破循环

当你的电脑突然毫无预警地自动重启&#xff0c;不仅打断了工作流程&#xff0c;还可能导致未保存的数据丢失&#xff0c;这无疑是一件令人沮丧的事情。那么&#xff0c;电脑自动重启是什么原因呢&#xff1f;有什么方法可以解决呢&#xff1f;别担心&#xff0c;在大多数情况下…

docker-2

27.构建python应用镜像-dockerfile实践项目 1.基于官方的镜像&#xff0c;构建python代码运行环境 dockerfile 2.运行镜像&#xff0c;开启一个读写的容器空间&#xff08;定制操作&#xff0c;将代码丢进去&#xff0c;运行调试&#xff09; 3.提交这个变化的容器层数据&#…

cal命令

1、命令详解&#xff1a; cal&#xff08;全称&#xff1a;Calendar&#xff09;该命令用来显示当前日历或者指定日期的公历。 2、官方参数&#xff1a; -1, --one 仅显示当前月份&#xff08;默认&#xff09;-3, --three 显示上个月、当前月和下个月-s, --sunday…

谷粒商城P85发布商品时规格参数不显示问题

P85讲&#xff0c;发布商品&#xff0c;点击下一步之后&#xff0c;发现规格参数不显示 打开控制台发现报错forEach...错误 查了问题原因&#xff0c;发现返回的分组中个别组的关联属性(attrs)可能为null 所以这个时候&#xff0c;需要确保后端返回的attrs不能为null 方式1…

数据结构之顺序存储线性表实现详解与示例(C,C#,C++)

文章目录 一、顺序存储线性表的基本概念二、顺序存储线性表的实现1、数据结构定义2、初始化3、添加元素4、访问元素5、修改元素6、删除元素7、销毁 三、示例C语言示例C#语言示例C语言示例 顺序存储线性表是一种基本的数据结构&#xff0c;它将线性表的元素按照一定的顺序存放在…

每天一个数据分析题(四百二十五)- 单因素方差分析

关于下表&#xff0c;错误说法是&#xff08; &#xff09; A. 这是单因素方差分析的输出结果 B. 表中 F< F crit, 与 P-value 大于显著性水平是等价的 C. 表内组间均方差没有显著大于组内均方差 D. 由于组内SS数值显著大于组间SS&#xff0c;因此可以推断不同分类对于…

机器学习——决策树(笔记)

目录 一、认识决策树 1. 介绍 2. 决策树生成过程 二、sklearn中的决策树 1. tree.DecisionTreeClassifier&#xff08;分类树&#xff09; &#xff08;1&#xff09;模型基本参数 &#xff08;2&#xff09;模型属性 &#xff08;3&#xff09;接口 2. tree.Decision…

最新开源免费数字人工具

使用步骤更是简单到不行&#xff1a; 1. 输入图片&#xff1a;选择你想要生成动态视频的肖像图片。 2. 输入音频&#xff1a;提供与图片匹配的音频文件&#xff0c;EchoMimic会根据音频内容驱动肖像的动态效果。 3. 设置参数&#xff1a;一般保持默认设置即可&#xff0c;当然&…

《梦醒蝶飞:释放Excel函数与公式的力量》11.4 ISERROR函数

第11章&#xff1a;信息函数 第四节 11.4 ISERROR函数 11.4.1 简介 ISERROR函数是Excel中的一个信息函数&#xff0c;用于检查指定单元格或表达式是否产生错误。如果单元格或表达式产生任何类型的错误&#xff08;如N/A、VALUE!、REF!等&#xff09;&#xff0c;则返回TRUE&…

全开源TikTok跨境商城源码/TikTok内嵌商城+搭建教程/前端uniapp+后端

多语言跨境电商外贸商城 TikTok内嵌商城&#xff0c;商家入驻一键铺货一键提货 全开源完美运营 海外版抖音TikTok商城系统源码&#xff0c;TikToK内嵌商城&#xff0c;跨境商城系统源码 接在tiktok里面的商城。tiktok内嵌&#xff0c;也可单独分开出来当独立站运营 二十一种…

leetcode165.解密数字

题目表述&#xff1a; 这道题目和斐波那契数列以及跳台阶问题十分相似。 斐波那契数列&#xff1a;0、1、1、2、3、5, 8、13、21、34 …… leetcode跳台阶问题&#xff1a;1、1、2、3、5, 8、13、21、34....... 这类题目的特点都是第N项的结果等于前两项的和。 但是解密数…

【深度学习】图形模型基础(7):机器学习优化中的方差减少方法(1)

摘要 随机优化是机器学习中至关重要的组成部分&#xff0c;其核心是随机梯度下降算法&#xff08;SGD&#xff09;&#xff0c;这种方法自60多年前首次提出以来一直被广泛使用。近八年来&#xff0c;我们见证了一个激动人心的新进展&#xff1a;随机优化方法的方差降低技术。这…

车载测试资料学习和CANoe工具实操车载项目(每日直播)

每日直播时间&#xff1a;&#xff08;直播方式&#xff1a;腾讯会议&#xff09; 周一到周五&#xff1a;20&#xff1a;00-23&#xff1a;00 周六与周日&#xff1a;9&#xff1a;00-17&#xff1a;00 向进腾讯会议学习的&#xff0c;可以关注我并后台留言 直播内容&#xff…

Simscape物理建模步骤

为了介绍构建和仿真物理模型的步骤&#xff0c;这里以simulink自带示例模型Mass-Spring-Damper with Controller为例&#xff0c;下图为建立好的模型。 详细物理建模和仿真分析步骤如下&#xff1a; 步骤 1&#xff1a;使用 ssc_new 创建新模型 使用 ssc_new 是开始构建 Sims…

李彦宏所说的卷应用到底是什么?

李彦宏在2024世界人工智能大会上的发言强调了一个重要的观点&#xff0c;那就是在AI时代&#xff0c;技术的应用比技术本身更为关键。他所提出的“卷应用”而非“卷模型”&#xff0c;实际上是在呼吁业界关注AI技术的实际落地和价值创造&#xff0c;而不是单纯地在模型精度或规…

案例|LabVIEW连接S7-1200PLC

附带&#xff1a; 写了好的参考文章&#xff1a; 通讯测试工具和博图仿真机的连接教程【内含图文完整过程软件使用】 解决博图V15 V16 V17 V18等高版本和低版本在同款PLC上不兼容的问题 目录 前言一、准备条件二、步骤1. HslCommunicationDemo问题1&#xff1a;连接失败?问题…

Lingo学习(二)——线性规划基础、矩阵工厂

一、线性规划基础 &#xff08;一&#xff09;方法 ① 一个线性规划中只含一个目标函数。(两个以上是多目标线性规划,Lingo无法直接解) ② 求目标函数的最大值或最小值分别用max …或min …来表示。 ③ 以!开头,以;结束的语句是注释语句; ④ 线性规划和非线性规划的本质…

01、Kerberos安全认证之原理及搭建命令使用学习笔记

文章目录 前言一、Kerberos原理1.1、数据安全防护&#xff08;kerberos所属的层次&#xff09;1.2、Kerberos介绍1.3、Kerberos名词介绍1.4、Kerberos术语1.5、Kerberos认证流程1.5.1、Kerberos流程图1.5.2、第一次通信&#xff1a;客户端与AS1.5.3、第二次通信&#xff1a;客户…

60、基于浅层神经网络的数据拟合(matlab)

1、基于浅层神经网络的数据拟合的简介、原理以及matlab实现 1&#xff09;内容说明 基于浅层神经网络的数据拟合是一种常见的机器学习方法&#xff0c;用于通过输入数据来拟合一个非线性函数。这种方法通常包括一个输入层、一个或多个隐藏层和一个输出层。神经网络通过学习权…

广电日志分析系统

需求 广电集团中有若干个系统都产生日志信息&#xff0c;目前大约分布与70到80台服务器中&#xff0c;分别是windows与Linux操作系统。需要将服务器上产生的日志文件利用我们的技术进行解析 设计 每个日志工作站负责30-50个服务器的日志解析工作。可以根据实际需求进行设置&…