如何使用TypeScript封装一个简单好用的Http工具

news2024/9/30 1:34:03

前言

Http 请求对于任何系统都是一大基石,那么如何封装一个高可用的 Http 请求工具呢?接下来手把手教你使用 TypeScript 封装一个高可用的 Http 请求工具。

本工具由三部分构成:Http基础层、基础方法层、业务层。

Http基础层

Http基础层主要用于统一处理错误和防抖。

防抖主要使用CancelToken来取消相同时间内的重复请求,通过Map对象来记录请求,在请求拦截器中取消重复请求,响应结束则删除请求记录。根据请求url&method&params&data

来判断是否是重复请求,并对一些特殊请求,设置白名单,不做去重处理。

import axios, {AxiosRequestConfig,AxiosResponse,AxiosError,AxiosInstance,
} from 'axios'
// 取消请求
const CancelToken = axios.CancelToken

interface HttpParams {BASEURL?: stringTIMEOUT?: numbererrorHandler?: (error: AxiosError, ctx?: AxiosInstance) => voidisCancel?: boolean
}

const cancelMap = new Map()
export default class Http {instance: AxiosInstanceconstructor({BASEURL,TIMEOUT = 1000 * 60 * 10,errorHandler = () => void 0,isCancel = true,}: HttpParams) {// 创建实例this.instance = axios.create({baseURL: BASEURL,timeout: TIMEOUT,withCredentials: true,})// 拦截请求this.instance.interceptors.request.use((config: AxiosRequestConfig) => {if (isCancel) {const key = uniqueKey(config)// if exists, abort itcancelHandler(key)if (!config.cancelToken && key) {config.cancelToken = new CancelToken((cancel) => {cancelMap.set(key, cancel)})}}return config},(error: AxiosError) => {if (!axios.isCancel(error)) {errorHandler(error, this.instance)return Promise.reject(error)}})// 拦截响应this.instance.interceptors.response.use(({ config, data, headers }: AxiosResponse) => {if (isCancel) {const key = uniqueKey(config)if (cancelMap.has(key)) {cancelMap.delete(key)}}if (config.method === 'head') {return Promise.resolve(headers)}return Promise.resolve(data)},(error: AxiosError) => {if (!axios.isCancel(error)) {errorHandler(error, this.instance)return Promise.reject(error)}})}static async get(url: string, config?: AxiosRequestConfig) {return (await axios.get(url, config))?.data}static async post( url: string,data?: Record<string, unknown>,config?: AxiosRequestConfig ) {return (await axios.post(url, data, config))?.data}static cancel(config: AxiosRequestConfig) {const key = uniqueKey(config)cancelHandler(key)}
}
// 对象转字符串
function obj2Str(obj: Record<string, unknown>) {let res = ''if (typeof obj !== 'object') {return res}try {res = JSON.stringify(obj)} catch {res = ''}return res
}
// 唯一ID
function uniqueKey(config: AxiosRequestConfig) {const bool = whiteList(config)return bool? '': `${config.method}-${config.url}-${obj2Str(config.params)}-${obj2Str(config.data)}`
}
// 取消请求
function cancelHandler(key: string) {if (key) {const cancel = cancelMap.get(key)if (cancel) {cancel()cancelMap.delete(key)}}
}
// 白名单,true 则不做取消
function whiteList(config: AxiosRequestConfig) {const { data, headers = {} } = configif (data instanceof FormData || headers.range) {return true}return false
} 

基础方法层

基于Http基础层,我们封装了基础方法层:支持我们常见的Http请求方法:getpostputdeletehead。这里增添了一个serviceName用来语义化接口,使得接口报错时更加友好。

// 基础get方法
const get = <T>( instance: AxiosInstance,url: string,serviceName = '未知服务',params = {},options = {} ) => {return new Promise<T>((resolve, reject) => {instance({url,method: 'get',params: params,...options,}).then((res) => {resolve(res?.data ? res.data : res)}).catch((error) => {reject(error)console.error(`get请求---${serviceName}---接口失败!`)})})
}
// 基础post方法
const post = <T>( instance: AxiosInstance,url: string,serviceName = '未知服务',data = {},options = {} ) => {return new Promise<T>((resolve, reject) => {instance({url,method: 'post',data: data,...options,}).then((res) => {resolve(res?.data ? res.data : res)}).catch((error) => {reject(error)console.error(`post请求---${serviceName}---接口失败!`)})})
}
// 基础put方法
const put = <T>( instance: AxiosInstance,url: string,serviceName = '未知服务',data = {},options = {} ) => {return new Promise<T>((resolve, reject) => {instance({url,method: 'put',data: data,...options,}).then((res) => {resolve(res?.data ? res.data : res)}).catch((error) => {reject(error)console.error(`put请求---${serviceName}---接口失败!`)})})
}
// 基础delete方法
const del = <T>( instance: AxiosInstance,url: string,serviceName = '未知服务',data = {},options = {} ) => {return new Promise<T>((resolve, reject) => {instance({url,method: 'delete',data: data,...options,}).then((res) => {resolve(res?.data ? res.data : res)}).catch((error) => {reject(error)console.error(`delete请求---${serviceName}---接口失败!`)})})
}
// 基础head方法
const head = <T>( instance: AxiosInstance,url: string,serviceName = '未知服务',params = {},options = {} ) => {return new Promise<T>((resolve, reject) => {instance({url,method: 'head',params: params,...options,}).then((res) => {resolve(res?.data ? res.data : res)}).catch((error) => {reject(error)console.error(`head请求---${serviceName}---接口失败!`)})})
} 

业务层

我们常用的请求有本地请求、业务后台请求、绝对路径请求以及其他三方后台请求,我们可以构建不同的axios实例来处理这些情况。

export interface AxiosConfig {BASEURL: {host: stringport: numberpath?: string}
}
interface InstanceMap {[key: string]: AxiosInstance | null
}
const instanceMap: InstanceMap = {base: null, // 当前系统ip下的请求business: null, // 后台接口请求
}
// 初始化默认实例
export const initAxiosInstance = (config: AxiosConfig) => {if (!config) returnconst {BASEURL: { host, port },} = configconst BASEURL = `//${host || location.hostname}:${port}/`instanceMap.base = new Http({BASEURL: BASEURL,errorHandler,}).instance
}
// 初始化后台实例
export const initBusinessInstance = (config: AxiosConfig) => {if (!config) returnconst {BASEURL: { host, port, path },} = configconst BASEURL = `//${host || location.hostname}:${port}/${path}`instanceMap.business = new Http({BASEURL: BASEURL,errorHandler,}).instance// set auth headersinstanceMap.business.defaults.headers.common['Authorization'] = `${getToken()}`
}

// 业务方法 GET
export const GET = <T>(url: string,serviceName?: string,params?: Record<string, any>,options?: Record<string, any>
): Promise<T> => {if (!instanceMap.base) {throw new Error('instanceMap.base is null')}return get<T>(instanceMap.base, url, serviceName, params, options)
}
// 业务方法 POST
export const POST = <T>(url: string,serviceName?: string,data?: Record<string, any>,options?: Record<string, any>
): Promise<T> => {if (!instanceMap.base) {throw new Error('instanceMap.base is null')}return post<T>(instanceMap.base, url, serviceName, data, options)
}
// 业务方法 PUT
export const PUT = <T>(url: string,serviceName?: string,data?: Record<string, any>,options?: Record<string, any>
): Promise<T> => {if (!instanceMap.base) {throw new Error('instanceMap.base is null')}return put<T>(instanceMap.base, url, serviceName, data, options)
}
// 业务方法 DELETE
export const DELETE = <T>(url: string,serviceName?: string,data?: Record<string, any>,options?: Record<string, any>
): Promise<T> => {if (!instanceMap.base) {throw new Error('instanceMap.base is null')}return del<T>(instanceMap.base, url, serviceName, data, options)
}
// 业务方法 HEAD
export const HEAD = <T>(url: string,serviceName?: string,params?: Record<string, any>,options?: Record<string, any>
): Promise<T> => {if (!instanceMap.base) {throw new Error('instanceMap.base is null')}return head<T>(instanceMap.base, url, serviceName, params, options)
} 

代码示例

在某些情况,我们可以需要动态修改后台接口hostport或者path。所以我们需要动态化配置。

1、根据环境动态选择配置

我们常见的是设置不同的env文件,在打包时通过DefinePlugin插件全局替换那些变量,达到不同环境设置不同的后台接口环境。

2、配置文件

上面那种虽然实现了根据不同环境构建,但是打包之后就不能更改后台环境了。如果打包之后需要更改后台环境呢?我们只能通过配置文件来配置后台环境,这样打包可以修改配置文件来配置不同的后台环境。我们只需要将配置文件放置在public目录即可。

import { GET, AxiosConfig } from './index'
export interface AppConfig {title: stringdevelopment?: AxiosConfigproduction?: AxiosConfig[key: string]: unknown | AxiosConfig
}
export function getAppConfig() {return GET<AppConfig>('static/appConfig.json', "获取系统配置文件")
} 
import {initAxiosInstance,initBusinessInstance,AxiosConfig,
} from './api/index';
import { getAppConfig, AppConfig } from './api/public'
const envList = ['development', 'production']
export default async () => {const config = await getAppConfig()const envConfig = config[import.meta.env.MODE] as AxiosConfiginitAxiosInstance(envConfig)initBusinessInstance(envConfig)
} 

总结

Http工具由三部分组成:Http基础层、基础方法层、业务层。Http基础层提供一个统一处理错误和防抖的axios实例;基础方法层提供了一些基础方法,支撑业务层;业务层通过传入不同的axios示例,构建不同的业务请求方法。

最后

为大家准备了一个前端资料包。包含54本,2.57G的前端相关电子书,《前端面试宝典(附答案和解析)》,难点、重点知识视频教程(全套)。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

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

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

相关文章

Lua语法入门

注意&#xff1a;文章将持续更新完善 文章目录一. 初识Lua二. HelloWorld三. Lua的数据类型四. 变量五. 循环六. 函数七. 条件控制一. 初识Lua Lua 是一种轻量小巧的脚本语言&#xff0c;用标准C语言编写并以源代码形式开放&#xff0c; 其设计目的是为了嵌入应用程序中&#…

VR全景行业的应用价值如何呈现?

互联网高速发展的今天&#xff0c;多媒体所包含的种类也是越来越多&#xff0c;而一些较为传统的表现方式已经越来越无法满足大部分客户对展示方式的要求。而在传统的表现方式中&#xff0c;展现的方式无非是静态的平面图片以及动态的视频&#xff0c;但是他们都有一个缺点就是…

MybatisPlus------BaseMapper<T>删除方法详解(三)

MybatisPlus------BaseMapper删除方法详解&#xff08;三&#xff09; MybatisPlus框架中&#xff0c;创建的mapper接口&#xff0c;需要继承BaseMapper接口,T代表与表对应的实体类。 BaseMapper接口中提供了对单表进行增删改查的基础方法。 下面分别介绍四种&#xff1a; int…

2023.2.15每日一题——867. 转置矩阵

每日一题题目描述解题核心解法一&#xff1a;二维表示 模拟解法二&#xff1a;一维表示 模拟题目描述 题目链接&#xff1a;867. 转置矩阵 给你一个二维整数数组 matrix&#xff0c; 返回 matrix 的 转置矩阵 。 矩阵的 转置 是指将矩阵的主对角线翻转&#xff0c;交换矩阵…

牛客教你用雇主品牌力抢人才!附6类校招玩法

最新校招数据显示&#xff0c;79%的应届生在Offer抉择时首要考量薪资福利。但谈钱多伤感情啊~牛客从100案例中挑出6种最潮的校招雇主品牌玩法&#xff0c;助力你抢人才。01、英特尔中国&#xff1a;“芯”动小镇雇主是否能让自己产生激情和热情&#xff0c;已经成为应届生选择O…

Sharding-jdbc读写分离

一、binlog日志查看是否开启binlog日志 show variables like log_%;注意&#xff1a;直接打开mysql-bin是乱码&#xff1b;修改my.ini配置binlog-row-imageFULLcharacter-set-serverutf8mb4default-character-setutf8mb4转成log打开首先需要切换到存放mysqlbinlog.exe应用程序的…

IDEA中使用tomcat8-maven-plugin插件

第一种方式 pom.xml <?xml version"1.0" encoding"UTF-8"?><project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.or…

android 混淆后的异常信息 工具处理

我们可以利用SDK中tools下的proguardgui.bat工具和混淆对应文档进行反混淆处理 D:\Android\sdk\tools\proguard\bin\proguardgui.bat 工具在SDK中的位置&#xff0c;有的SDK版本不同这个工具的具体位置可能有改变&#xff0c;也可以在tools中直接搜索proguardgui.bat&#x…

Netty 入门

文章目录一、概述1.1 Netty 是什么&#xff1f;1.2 Netty 的地位1.3 Netty 的优势二、Hello World2.1 目标2.2 服务器端2.3 客户端2.4 流程梳理三、组件3.1 EventLoop3.2 演示 NioEventLoop 处理 io 事件3.3 演示 NioEventLoop 处理普通任务3.4 演示 NioEventLoop 处理定时任务…

ThingsBoard-规则链-check existence fields

1、概述 今天我主要讲解【check existence fields】规则节点,顾名思义,这个节点就是检测字段是否存在。 可以检查规则节点消息(msg)和元数据(metadata)的字段。 2、节点理解 2.1、概述 【check existence fields】节点如图所示: 名称:你需要给这个节点命名。 Mes…

spring security简单教程以及实现完全前后端分离

简单教程spring security是spring家族的一个安全框架&#xff0c;入门简单。对比shiro&#xff0c;它自带登录页面&#xff0c;自动完成登录操作。权限过滤时支持http方法过滤。在新手入门使用时&#xff0c;只需要简单的配置&#xff0c;即可实现登录以及权限的管理&#xff0…

微服务 分布式搜索引擎 Elastic Search 索引库与文档操作

文章目录⛄引言一、Elastic Search 索引库⛅mapping 映射属性二、索引库的 CRUD &#xff08;增删改查&#xff09;⏰索引库的创建和映射⚡对索引库进行查询、修改、删除操作四、Elastic Search 文档操作⌚新增、查询、删除文档⚡修改Elastic Search 文档⛵小结⛄引言 本文参考…

C++——类和对象2

目录 类的六个默认成员函数 1. 构造函数 1.1 特性 1.2 实现 1.3 默认构造函数 1.4 默认构造函数处理行为 1.5 初始化列表 1.6 explicit关键字 2. 析构函数 2.1 特性 2.2 实现 2.3 默认析构函数 2.4 对象析构的顺序 3. 拷贝构造函数 3.1 特性 3.2 拷贝构造的…

JVM_内存区域与内存溢出异常

文章目录一、运行时数据区域1、程序计数器2、Java虚拟机栈3、本地方法栈4、Java堆5、方法区6、运行时常量池7、直接内存二、HotSpot虚拟机对象1、对象的创建2、对象的内存布局3、对象的访问定位三、OutOfMemoryError和StackOverflowError异常1、Java堆溢出&#xff08;最常见&a…

闭包可能导致的内存泄漏

什么是闭包 闭包是函数可以保留和访问其外部变量&#xff0c;比如 let a 1 let b function() {console.log(a) }这里变量b指向的函数可以访问外面的变量&#xff0c;你会说这不是废话吗&#xff1f;函数都可以访问外部变量呀 那再看一个例子 function f() {let value 123…

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册

人人都能看懂的Spring源码解析&#xff0c;扫描加载BeanDefinition的过程原理解析什么是BeanDefinition&#xff1f;两种配置方式扫描并读取配置信息&#xff0c;解析成BeanDefinition保存BeanDefinition源码走读xml配置方式整体流程示例代码BeanDefinition加载解析的入口创建X…

机器学习基础

一、基本概念 1 学习的概念 1975年图灵奖获得者、1978年诺贝尔经济学奖获得者、著名学者赫伯特.西蒙 (Herbert Simon) 曾下过一个定义: 如果一个系统&#xff0c;能够通过执行某个过程&#xff0c;就此改进了它的性能&#xff0c;那么这个过程就是学习.由此可看出&#xff0c;…

思科基础组面试(部分)

面了三轮&#xff0c;前面两轮因为录的视频坏了&#xff0c;很多问题忘了。 Round 1 Q:举例说明为什么hashmap线程不安全 A1: JDK1.8 HashMap线程不安全体现在&#xff1a;数据覆盖: 其中第六行代码是判断是否出现hash碰撞&#xff0c;假设两个线程A、B都在进行put操作&#…

软件测试未来发展趋势怎么样

未来&#xff0c;互联网技术是很多企业能够活下去的关键点。互联网技术成为新的基建&#xff0c;互联网“基建”化就决定了软件测试行业的缺口会一直扩大。 并且&#xff0c;软件测试岗位&#xff0c;已不仅局限于互联网企业&#xff0c;现已逐步深入到实体产业&#xff0c;金…

【安全等保】安全等保二级和三级哪个高?哪个费用更高?

等保政策已经严格落地执行了&#xff0c;各大企业纷纷接到了过等保的通知&#xff0c;但有的估计是第一次听到等保&#xff0c;对于等保相关政策都是非常蒙圈的。这不不少企业相关负责人在问&#xff0c;安全等保二级和三级哪个高&#xff1f;哪个费用更高&#xff1f;这里我们…