[项目构建] 二次封装统一Axios配置 JSTS两个版本实现取消重复请求,超时重发

前言

搭建项目时,每个项目都会axios来发请求,但是很多地方都需要发送请求,需要二次封装来解决可能需要处理一些通用的逻辑,比如统一的错误处理、请求拦截、响应拦截、设置请求头等。

axios二次封装基本需要四个方面

全局配置

Token、密钥

响应的统一处理

封装请求方法

全局配置

经常用的配置

baseURL:baseURL 将自动加在 url 前面,除非 url 是一个绝对 URL

timeout: timeout 指定请求超时的毫秒数, 如果请求时间超过 timeout 的值,则请求会被中断

responseType:表示浏览器将要响应的数据类型 , 选项包括: ‘arraybuffer’, ‘document’, ‘json’, ‘text’, ‘stream’

haeaders :自定义请求头

withCredentials: 表示跨域请求时是否需要使用凭证

let request= axios.create({

baseURL: "http://localhost:8000/",

timeout: 30 * 1000,

responseType: "json",

headers:{
"test": "this is globally configured request header"
},
withCredentials: true,
})

在请求拦截器中加入Token,密钥等 用md5加密一下

出于权限和安全考虑可能需要密钥
把他们在请求拦截器中设置到请求头

//使用axios的请求拦截器
request.interceptors.request.use((config)=> {
// token 一般存在于vuex/pinia或localStorage
let token = localStorage.getItem('token')
let url= config.url
if(!global.whiteListURl.includes(url) && token){
config.headers.token = token
}
// token一般是明文存储安全性不够,所以还有可能要设置密钥
const secretId = md5(global.secretId + new Date().toString())
config.headers.secretId = secretId
return config
},error => {
// 做个错误处理
return Promise.reject(new Error(error))
})

响应的统一基本处理,针对于错误的情况做统一全局处理

//使用axios响应拦截器
request.interceptors.request.use((res) => {
// 响应得做统一处理
const status = res.data.code || 200 ;

const message = res.data.message || 'No messsage'

//if(status === 401){

//alert('你没有权限')

// 设置你要跳转的页面 

//router.push("/index")

//return Promise.reject(new Error(message))

//}

// ....各种错误码不多举例了

if(status !==200){

alert(`错误码${status}+${message}`)

return Promise.reject(new Error(message))

}

},error =>{

alert('error')

return Promise.reject(new Error(error))

})
封装请求方法

把对接口的请求封装为方法

request.get=function (url,params,__object={}){
	return axios.get(url,{...params,__object})
}
request.post=function(url,params,__object){
	return axios.post(url, params, __object)
}
request.put = function(url,params,__object){
	return axios.put(url,parms,__object)
}

request.delete= function(url,params,__object){
	return axios.delete(url,{parms,__object})
}
request.download = fucntion (url,params,__object){
	reutnr axios.post(url,params,{ ..._object, responseType: 'blob'})
}
...
基本封装四大项思路已经完成

我们可以用class类包装一下这四大项

为什么使用class类

通过将 Axios 实例和相关方法封装在一个类中,可以更清晰地组织代码,提高代码的可维护性和可读性,更好导出使用。

类可以实例化为对象,每个对象都有自己的状态和行为。这使得在应用程序中可以轻松地创建多个独立的客户端实例,并在它们之间共享或隔离状态。

import axios from "axios";
import global from '../global/index.ts'
import md5 from 'md5'
const config = {
// 使用别人的接口实验下 先把baseURL关闭
// baseURL: "http://localhost:8000/",

timeout: 30 * 1000,

responseType: "json",

headers:{

"test": "this is globally configured request header"

},

// withCredentials: true,

}

class RequestHttp {

//实例对象

service;

constructor(config) {

//

this.service = axios.create(config);

//请求拦截器

this.service.interceptors.request.use(

(config) => {

// token 一般存在于vuex/pinia或localStorage

let token = localStorage.getItem("token");

let url = config.url;

if (!global.whiteListURl.includes(url) && token) {

config.headers.token = token;

}

// console.log(config.headers)

const secretId = md5(global.secretId + new Date().toString());

config.headers.secretId = secretId;

return config;

// token一般是明文存储安全性不够,所以还有可能要设置密钥

},

(error) => {

// 做个错误处理

return Promise.reject(new Error(error));

}

);

//使用axios响应拦截器

this.service.interceptors.request.use(

(res) => {

// 响应得做统一处理


const status = res.code || 200;

const message = res.msg || "No messsage";

  

if (status === 401) {

alert("你没有权限");

// 设置你要跳转的页面

// router.push("/index");

return Promise.reject(new Error(message));

}

// ....各种错误码不多举例了

if (status !== 200) {

alert(`错误码${status}+${message}`);

return Promise.reject(new Error(message));

}

return res

},

// 这里的错误返回可以具体些

async (error) => {

// 请求超时 && 网络错误单独判断,没有 response

if (error.message.indexOf("timeout") !== -1)

alert("请求超时!请您稍后重试");

if (error.message.indexOf("Network Error") !== -1)

alert("网络错误!请您稍后重试");

// 服务器结果都没有返回(可能服务器错误可能客户端断网),断网处理:可以跳转到断网页面

if (!window.navigator.onLine) router.replace("/500");

return Promise.reject(error);

}

);

}

get(url, params?, _object = {}) {

return this.service.get(url, { params, ..._object });

}

post(url, params, _object = {}) {

return this.service.post(url, params, _object);

}

put(url, params, _object = {}) {

return this.service.put(url, params, _object);

}

delete(url, params, _object = {}) {

return this.service.delete(url, { params, ..._object });

}

download(url, params, _object = {}) {

return this.service.post(url, params, { ..._object, responseType: "blob" });
}
}
export default new RequestHttp(config)

接下来 我们可以加入取消频繁重复请求的功能

原理很简单,定义一个数据结构存储url(或者更精确的),请求完再删掉,如果之后的请求url还存在就报错就行

这个功能有很多应用

function cancelRepaeatRequest(config){
	let hasRequest = []
	return ()=>{
		if(hasRequest.indexOf(config.url) !== -1){
			return Promise.reject(new Error('不要急'))
		}
		hasRequest.push(config.url)
		return request({...config}).then(
			()=>{
					hasRequest = hasRequest.filter((item)=>{
					if(item !== config.url){
						return item
					}
				})
			}
		)
		)
	}
}
不过有更成熟的实现,其核心是通过 AbortController API来取消请求。

那么AbortController是什么呢
AbortController 是一个 JavaScript 中的内置对象,用于控制异步操作的中止,也用于取消正在进行的网络请求

//封装
const pendingMap = new Map()

// 创建各请求唯一标识, 返回值类似:'/api:get',后续作为pendingMap的key

const getUrl = (config) => {

return [config.url, config.method].join(':')

}

class AbortAxios {

// 添加控制器

addPending(config) {

this.removePending(config)

const url = getUrl(config)

// 创建控制器实例

const abortController = new AbortController()

// 定义对应signal标识

config.signal = abortController.signal

if (!pendingMap.has(url)) {

pendingMap.set(url, abortController)

}

}

// 清除重复请求

removePending(config) {

const url = getUrl(config)

if (pendingMap.has(url)) {

// 获取对应请求的控制器实例

const abortController = pendingMap.get(url)

// 取消请求

abortController?.abort()

// 清除出pendingMap

pendingMap.delete(url)

}

}

}
export default new AbortAxios
在刚刚RequestHttp类中加入代码
//在请求拦截器加上
abortAxios.addPending(config)
//在响应拦截器加上
res && abortAxios.removePending(res.config)

接下来我们实现下超时重发

超时重发的意思是全局配置timeout配置后,如果请求超时会自动重新发送

那么如何知道请求超时错误呢

响应拦截器中错误err.message中有错误信息

error.message.indexOf("timeout") !== -1 使用这个来判断超时错误

实现起来也很简单 设置等待时间和重新发新发送次数再用Promise.then加定时器让请求在发送一次
在响应拦截器中的错误

//在响应拦截器中
if (error.message.indexOf("timeout") !== -1){

const { waitTime, count } = { waitTime: 1000, count: 1};

return retry(this.service, error, waitTime, count);

// alert("请求超时!请您稍后重试");

}
//新建文件axiosRetry


export default function retry(instance, error, waitTime, count) {

const config = error.config;

// 当前重复请求的次数
config.currentCount = config.currentCount ?? 0;
console.log(`${config.currentCount}次重连`);

// 如果当前的重复请求次数已经大于规定次数,则返回Promise
if (config.currentCount >= count) {
return Promise.reject(error);
}
config.currentCount++;
// 等待间隔时间结束后再执行请求
return wait(waitTime).then(() => instance(config));
}
function wait(waitTime: number) {
return new Promise(resolve => setTimeout(resolve, waitTime));
}

最终实现TS版本 TS版本就是把上述代码加了类型规范

import {

AxiosInstance,

AxiosError,

AxiosRequestConfig,

InternalAxiosRequestConfig,

AxiosResponse,

} from 'axios'

import axios from "axios";

import global from '../global/index.ts'

import md5 from 'md5'

import abortAxios from './config/index.js'

const config = {

baseURL: "http://localhost:8000/",

timeout: 30 * 1000,

headers:{

"test": "this is globally configured request header"

},

// withCredentials: true,

}

class RequestHttp {

//实例对象

service: AxiosInstance;

constructor(config:AxiosRequestConfig) {

//

this.service = axios.create(config);

//请求拦截器

this.service.interceptors.request.use(

(config:InternalAxiosRequestConfig) => {

abortAxios.addPending(config)

// token 一般存在于vuex/pinia或localStorage

let token = localStorage.getItem("token");

let url = config.url;

if (!global.whiteListURl.includes(url) && token) {

config.headers.token = token;

}

const secretId = md5(global.secretId + new Date().toString());

config.headers.secretId = secretId;

// abortAxios.removePending(config)

return config;

// token一般是明文存储安全性不够,所以还有可能要设置密钥

},

(error:AxiosError) => {

// 做个错误处理

return Promise.reject(error);

}

);

//使用axios响应拦截器

this.service.interceptors.response.use(

(res:AxiosResponse) => {

// 响应得做统一处理

res && abortAxios.removePending(res.config)

const {data} = res

const status = data.code || 200;

const message = data.message || "No messsage";

// ....各种错误码不多举例了

if (status !== 200) {

alert(`错误码${status}+${message}`);

return Promise.reject(new Error(message));

}

return res

},

// 这里的错误返回可以具体些

async (error:AxiosError) => {

// 请求超时 && 网络错误单独判断,没有 response

if (error.message.indexOf("timeout") !== -1)
const { waitTime, count } = { waitTime: 1000, count: 1};
return retry(this.service, error, waitTime, count);
//alert("请求超时!请您稍后重试");

if (error.message.indexOf("Network Error") !== -1)

alert("网络错误!请您稍后重试");

// 服务器结果都没有返回(可能服务器错误可能客户端断网),断网处理:可以跳转到断网页面

// if (!window.navigator.onLine) router.replace("/500");

return Promise.reject(error);

}

);

}

get<T>(url:string, params:object, _object = {}):Promise<T> {

return this.service.get(url, { params, ..._object });

}

post<T>(url:string, params:object| string, _object = {}):Promise<T> {

return this.service.post(url, params, _object);

}

put<T>(url:string, params:object, _object = {}):Promise<T> {

return this.service.put(url, params, _object);

}

delete<T>(url:string, params:any, _object = {}):Promise<T> {

return this.service.delete(url, { params, ..._object });

}

download(url:string, params:object, _object = {}):Promise<BlobPart> {

return this.service.post(url, params, { ..._object, responseType: "blob" });

}

}

export default new RequestHttp(config)

文章到这里就结束了,希望对你有所帮助

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

零基础HTML教程(31)--HTML5多媒体

文章目录 1. 背景2. audio音频3. video视频4. audio与video常用属性5. 小结 1. 背景 在H5之前&#xff0c;我们要在网页上播放音频、视频&#xff0c;需要借助第三方插件。 这些插件里面最火的就是Flash了&#xff0c;使用它有几个问题&#xff1a; 首先要单独安装Flash&…

华为Pura 70系列,一种关于世界之美的可能

1874年&#xff0c;莫奈创作了《印象日出》的油画&#xff0c;在艺术界掀起了一场革命。当时的主流艺术&#xff0c;是追求细节写实&#xff0c;追求场面宏大的学院派。他们称莫奈等人是“印象派”&#xff0c;认为莫奈的画追求光影表达&#xff0c;追求描绘抽象的意境&#xf…

echarts地图叠加百度地图底板实现数据可视化

这里写自定义目录标题 echarts地图叠加百度地图实现数据可视化echarts地图叠加百度地图实现数据可视化 实现数据可视化时,个别情况下需要在地图上实现数据的可视化,echarts加载geojson数据可以实现以地图形式展示数据,例如分层设色或者鼠标hover展示指标值,但如果要将echa…

【Redis 开发】一人一单,超卖问题(悲观锁,乐观锁,分布式锁)

锁 悲观锁乐观锁第一种&#xff1a;版本号法第二种&#xff1a;CAS法实现乐观锁 悲观锁与乐观锁的比较 一人一单分布式锁Redis实现分布式锁 悲观锁 认为线程问题一定会发生&#xff0c;因此在操作数据库之前先获取锁&#xff0c;确保线程串行执行&#xff0c;例如Synchronized…

好的猫咪主食冻干到底该咋选?品控稳定的主食冻干推荐

315中国之声报道的河北省邢台市南和区某宠粮代工厂的“行业潜规则”&#xff0c;给各位铲屎官拉响了警钟。配料表上写的鸡肉含量为52%&#xff0c;新鲜鸡小胸含量为20%&#xff0c;所谓的鲜鸡肉其实就是鸡肉粉。本来养宠物是为了让自己身心愉悦&#xff0c;但这样的行业乱象弄得…

prompt提示词:AI英语词典优化版Pro,让AI教你学英语,通过AI实现一个网易有道英语词典

目录 一、前言二、效果对比三、优化《AI英语词典》提示词四、其他获奖作品链接 一、前言 不可思议&#xff01;我的AI有道英语字典助手竟然与百度千帆AI应用创意挑战赛K12教育主题赛榜首作品差之毫厘 &#xff0c;真的是高手都是惺惺相惜的&#xff0c;哈哈&#xff0c;自恋一…

发票管理设计方案

1、背景介绍 在供应链金融业务场景下&#xff0c;供应商可以依赖与大型企业的合同、发票信息&#xff0c;到金融机构进行融资。本文探讨发票管理的设计方案。 2、需求分析 如上图所示&#xff0c;发票管理主要分为发票信息的管理以及发票可用余额管理2个部分。 名词解释&…

Docker-概念及配置(超详细)

docker 第一章 1、什么是docker 答&#xff1a;docker是一种容器引擎&#xff0c;通过docker可以将软件安装并且配置好以后&#xff0c;做成一个镜像文件。通过这个镜像文件可以快速的安装、配置软件环境 2、3个概念 【docker镜像】&#xff1a;将软件环境安装配置好以后产生…

【QA】Git的底层原理

前言 本文通过一个简单的示例&#xff0c;来理解Git的底层原理。 示例 1、新建本地仓库并上传第一个文件 相关步骤&#xff1a; 新建仓库及创建文件查看文件状态将文件添加到暂存区将文件提交到本地仓库 HMTeenLAPTOP-46U4TV6K MINGW64 /d/GSF_Data/Github/Java/Git/git-…

一张图带你理解 绝对路径 和 相对路径

绝对路径和相对路径是用于定位文件或目录位置的两种不同方式。 1、绝对路径&#xff1a; 绝对路径是从文件系统的根目录开始的完整路径&#xff0c;可以唯一地标识文件或目录的位置。 绝对路径是以根目录开始的 在Unix/Linux系统中&#xff0c;绝对路径是类似于/home/user/do…

2024 OceanBase 开发者大会:OceanBase 4.3正式发布,打造近PB级实时分析数据库

4月20日&#xff0c;2024 OceanBase开发者大会盛大召开&#xff0c;吸引了50余位业界知名的数据库专家和爱好者&#xff0c;以及来自全国各地的近600名开发者齐聚一堂。他们围绕一体化、多模、TP与AP融合等前沿技术趋势展开深入讨论&#xff0c;分享场景探索的经验和最佳实践&a…

基于DEAP数据集的四种机器学习方法的情绪分类

在机器学习领域&#xff0c;KNN&#xff08;K-Nearest Neighbors&#xff09;、SVM&#xff08;Support Vector Machine&#xff09;、决策树&#xff08;Decision Tree&#xff09;和随机森林&#xff08;Random Forest&#xff09;是常见且广泛应用的算法。 介绍 1. KNN&am…

Let‘s Move Sui:解锁区块链高性能潜力,探索创新开发体验

Sui 是基于第一原理重新设计和构建而成的 L1 公链&#xff0c;旨在为创作者和开发者提供能够承载 Web3 中下一个十亿用户的开发平台。 今年&#xff0c;Sui 的原生编程语言 Move 迎来了重要的更新升级。2024 版将增加枚举 Enums、宏函数、Method 语法等功能。这些重要的新功能为…

2024.4.28 机器学习周报

目录 引言 Abstract 文献阅读 1、题目 2、引言 3、创新点 4、总体流程 5、网络结构 5.1、损失函数 5.2、Confidence Maps 5.3、Part Affinity Fields(PAFs) 5.4、多人的PAFs 6、实验 7、结论 深度学习 yolov8实现目标检测和人体姿态估计 Yolov8网络结构 yaml…

基于深度学习的实时人脸检测与情绪分类

情绪分类 实时人脸检测与情绪分类 Kaggle Competion 数据集 fer2013 中的测试准确率为 66%CK数据集的检验准确率为99.87%情绪分类器模型预测从网络摄像头捕获的实时视频中的平均成本时间为 4~ 10ms 关键技术要点&#xff1a; 实时人脸检测&#xff1a;系统采用了前沿的人脸检…

案例-部门管理-新增

黑马程序员JavaWeb开发教程 文章目录 一、页面原型二、接口文档三开发1、controller2、service&#xff08;1&#xff09;service接口层&#xff08;2&#xff09;Service实现层 3、 mapper4、postman 优化 一、页面原型 二、接口文档 在这里插入图片描述 三开发 1、control…

2024年好用又便宜的云手机!哪款性价比高?

随着科技的飞速发展&#xff0c;云计算技术也在不断演进&#xff0c;而云手机作为其创新之一&#xff0c;已经开始在我们的生活中崭露头角。它通过将手机的硬件和软件功能移到云端&#xff0c;让用户能够借助强大的云计算资源完成各种任务。2024年&#xff0c;哪款云手机性价比…

运行django

确保app被注册 urls.py中编写url 视图对应关系 命令行启动 python manage.py runserver

“湘”约你我,“V”你而来!苏州金龙新V系客车闪耀星城

“湘”约你我、为你而来&#xff01;4月24日&#xff0c;苏州金龙新V系智慧客车推介会走进星城长沙。来自湖南省内的160余位旅游客运行业协会及企业代表齐聚一堂&#xff0c;共同见证客车行业新质生产力标杆产品的无限魅力。 当前&#xff0c;湖南的旅游产业和道路运输业正处于…

每年首版次测试报告的要求有哪些?

每年首版次测试报告的要求可能因不同的地区、行业或产品而有所差异&#xff0c;但一般而言&#xff0c;它们通常遵循一些基本的标准和原则。以下是一些常见的首版次测试报告要求&#xff1a; 完整性&#xff1a;测试报告应包含所有必要的测试内容&#xff0c;包括但不限于测试…
最新文章