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/diannao/43215.shtml

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

相关文章

实用性提升百分之一百!!!【ONLYOFFICE 8.1版本】全方位深度性能测评

目录 【ONLYOFFICE 8.1 版本】全方位深度性能测评 一、界面与用户体验 二、文字处理功能 表格处理功能 演示文稿功能 协作与共享功能 性能与稳定性 总结 【ONLYOFFICE 8.1 版本】全方位深度性能测评 在当今数字化办公的时代&#xff0c;办公软件的选择对于提高工作效率和…

selenium处理cookie问题实战

1. cookie获取不完整 需要进入的资损平台(web)首页&#xff0c;才会出现有效的ctoken等信息 1.1. 原因说明 未进入指定页面而获取的 cookie 与进入页面后获取的 cookie 可能会有一些差异&#xff0c;这取决于网站的具体实现和 cookie 的设置方式。 通常情况下&#xff0c;一些…

【解决Windows11系统Windows Hello不能使用的问题】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、Windows Hello是什么&#xff1f;二、使用步骤1.购买一个摄像头2.开始配置 三、异常解决1.内置管理员不能使用2.没找到合适的摄像头3.摄像头需要专用驱动4.…

原创作品——教育课程界面设计

教育行业UI界面设计需直观易懂&#xff0c;确保学习者能迅速上手&#xff0c;减少认知负担。布局清晰&#xff0c;导航便捷&#xff0c;功能按钮和图标设计应符合教育场景&#xff0c;直接支持学习目标的达成&#xff0c;促进高效学习体验。 通过丰富的互动元素&#xff08;如拖…

博途通讯笔记1:1200与1200之间S7通讯

目录 一、添加子网连接二、创建PUT GET三、各个参数的意义 一、添加子网连接 二、创建PUT GET 三、各个参数的意义

代码随想录(day1)二分法

if语句的基本语法 if 要判断的条件: 条件成立的时候&#xff0c;要做的事举例&#xff1a; if nums[middle]<target:leftmiddle1 while语句的基本语法&#xff1a; while 判断条件(condition)&#xff1a;执行语句(statements)举例&#xff1a; while left<right:midd…

2通道音频ADC解码芯片ES7243L、ES7243E、ES7243,用于低成本实现模拟麦克风转换为IIS数字话筒

前言&#xff1a; 音频解码芯片某创参考价格&#xff1a; ES7243L 500&#xff1a;&#xffe5;1.36 / 个 ES7243E 500&#xff1a;&#xffe5;1.66 / 个 ES7243 500&#xff1a; &#xffe5;1.91 / 个 其中ES7243L工作电压为1.8V&#xff0c;与其他两款的3.3V工作电压不同&…

日本IT-SIER/SES的区别详情、契约形态等

一、SLER 主要的服务内容就是“帮客人开发系统或是各种APP&#xff0c;并在指定期间内交货&#xff0c;交货后也会持续进行运维等售后服务”。 客人很广泛&#xff0c;小到普通的服务业商家&#xff08;餐饮店/服饰店/美容业/电商&#xff09;大到各种公共/政府机关&#xff…

【面试题】串联探针和旁挂探针有什么区别?

在网络安全领域中&#xff0c;串联探针和旁挂探针&#xff08;通常也被称为旁路探针&#xff09;是两种不同部署方式的监控设备&#xff0c;它们各自具有独特的特性和应用场景。以下是它们之间的主要区别&#xff1a; 部署方式 串联探针&#xff1a;串联探针一般通过网关或者…

第一百四十九节 Java数据类型教程 - Java子字符串、字符串转换

Java数据类型教程 - Java子字符串 获取子字符串 我们可以使用substring()方法来获取字符串的子部分。 我们可以将开始索引作为参数&#xff0c;并返回一个从开始索引开始到字符串结尾的子串。 我们还可以将开始索引和结束索引作为参数。 它返回从开始索引开始的子字符串和小…

Windows右键没有新建Word、PPT与Excel的解决方法

本文介绍在Windows电脑中&#xff0c;右键与资源管理器的“新建”选项中&#xff0c;都没有新建Word、PPT或Excel文件的解决方法。 最近&#xff0c;发现一台重装了系统与Office的电脑中&#xff0c;无论是桌面上与资源管理器中的右键&#xff0c;还是资源管理器左侧顶部的“新…

手写简单模拟mvc

目录结构&#xff1a; 两个注解类&#xff1a; Controller&#xff1a; package com.heaboy.annotation;import java.lang.annotation.*;/*** 注解没有功能只是简单标记* .RUNTIME 运行时还能看到* .CLASS 类里面还有&#xff0c;构建对象久没来了&#xff0c;这个说明…

C++编译链接原理

从底层剖析程序从编译到运行的整个过程 三个阶段 一、编译阶段二、链接阶段三、运行阶段 为了方便解释&#xff0c;给出两端示例代码&#xff0c;下面围绕代码进行实验&#xff1a; //sum.cpp int gdata 10; int sum(int a,int b) {return ab; }//main.cpp extern int gdata…

初始redis:在Ubuntu上安装redis

1.先切换到root用户 使用su命令切换到root 2.使用apt命令来搜索redis相关的软件包 命令&#xff1a;apt search redis 3.下载redis 命令&#xff1a; apt install redis 在Ubuntu 20.04中 &#xff0c;下载的redis版本是redis5 4.查看redis状态 命令&#xff1a; netst…

Python自动化测试系列[v1.0.0][高效自动化设计]

Python多线程应用于自动化测试 将多线程在测试巧妙地应用&#xff0c;确实会带来很多好处&#xff0c;并且这是充分利用机器资源执行高效率测试很好的方式 # -*- coding: utf-8 -*- import threading from time import ctime import time from selenium import webdriverdef …

LLM 入门与实践(四) Yi 部署与分析

本文截取自20万字的《PyTorch实用教程》&#xff08;第二版&#xff09;&#xff0c;敬请关注&#xff1a;《Pytorch实用教程》&#xff08;第二版&#xff09;无论是零基础入门&#xff0c;还是CV、NLP、LLM项目应用&#xff0c;或是进阶工程化部署落地&#xff0c;在这里都有…

python对象

类 我们目前所学习的对象都是Python内置的对象但是内置对象并不能满足所有的需求&#xff0c;所以我们在开发中经常需要自定义一些对象类&#xff0c;简单理解它就相当于一个图纸。在程序中我们需要根据类来创建对象类就是对象的图纸&#xff01;我们也称对象是类的实例&#…

JAVA--IO流

一、IO流什么&#xff1f; I/O是Input/output的缩写&#xff0c;用于处理设备之间的数据传输。如读/写文件、网络通讯。 java程序中&#xff0c;对于数据的输入、输出是以流&#xff08;Stream)的方式进行。 Java.io包下提供了各种流类和接口&#xff0c;用以获取不同种类的数…

ELfK logstash filter模块常用的插件 和ELFK部署

ELK之filter模块常用插件 logstash filter模块常用的插件&#xff1a; filter&#xff1a;表示数据处理层&#xff0c;包括对数据进行格式化处理、数据类型转换、数据过滤等&#xff0c;支持正则表达式 grok 对若干个大文本字段进行再分割成一些小字段 (?<字段名…

【算法篇】KMP算法,一种高效的字符串匹配算法

我们今天了解一个字符串匹配算法-KMP算法&#xff0c;内容难度相对来说较高&#xff0c;建议先收藏再细品&#xff01;&#xff01;&#xff01; KMP算法的基本概念 KMP算法是一种高效的字符串匹配算法&#xff0c;由D.E.Knuth&#xff0c;J.H.Morris和V.R.Pratt提出的&#…