TS二次封装axios学习总结

news/2025/3/3 4:47:40/

1.put请求

put请求一般用的很少,主要有两点区别。

1.PUT请求:如果两个请求相同,后一个请求会把第一个请求覆盖掉。(所以PUT用来改资源)
Post请求:后一个请求不会把第一个请求覆盖掉。(所以Post用来增资源)
2.PUT通常指定了资源的存放位置,而POST则没有,POST的数据存放位置由服务器自己决定。

2.ts内置高级对象Record

用法:getHourCheckinList(params?: Record<string, any>){}
Record 是 TS 内置的一个高级类型,是通过映射类型的语法来生成索引类型的,Record<string, any> 也就是 key 为 string 类型,value 为任意类型的索引类型,可以代替 object 来用,用来指定object内部的key value的数据类型。
其他高级内置对象

http.ts 二次封装axios:

import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios'
import { ElMessage } from 'element-plus'
import { loginApi } from '@/apis'
import type { ApiResponse, RefreshParams, RefreshData, HttpResponse } from '../../@types/api'// 定义全局请求配置接口
interface HttpClientOptions {baseURL?: stringheaders?: Record<string, string>timeout?: number
}// 定义特殊错误码的处理类型
type CodeHandler = (error: ApiResponse['err']) => void// 定义重试配置接口
interface RetryConfig {maxRetries: number;          // 最大重试次数retryDelay: number;          // 重试延迟时间retryAbleStatus: number[];   // 可重试的状态码
}// 通用请求类
class HttpClient {private instance: AxiosInstanceprivate isRefreshing = false // 用于防止同时刷新多个 tokenprivate refreshSubscribers: Array<(token: string) => void> = []private codeHandlers: Record<number, CodeHandler> = {} // 用于存储特殊错误码的处理器private customHeaders: Record<string, string | number> = {} // 存储自定义请求头private retryConfig: RetryConfig = {maxRetries: 3,retryDelay: 1000,retryAbleStatus: [408, 500, 502, 503, 504]}// 添加设置自定义请求头的方法public setCustomHeaders(headers: Record<string, string | number>) {this.customHeaders = { ...this.customHeaders, ...headers };}constructor(options: HttpClientOptions) {this.instance = axios.create({baseURL: options.baseURL,headers: options.headers || {},       // 默认请求头timeout: options.timeout || 15000,     // 请求超时时间(默认10秒)withCredentials: false // 不携带 cookie,避免 CORS 问题})// 请求拦截器this.instance.interceptors.request.use((config: InternalAxiosRequestConfig) => {// 获取 tokenconst token = localStorage.getItem('token')// 如果有 token,添加到请求头if (token) {config.headers.Authorization = `${token}`}// 添加通用请求头config.headers['Content-Type'] = 'application/json'config.headers.Accept = 'application/json'// 从 localStorage 获取 tenant-id 和 hotel-idconst tenantId = localStorage.getItem('tenant-id')const hotelId = localStorage.getItem('hotel-id')if (tenantId) {config.headers['tenant-id'] = tenantId}if (hotelId) {config.headers['hotel-id'] = hotelId}// 添加自定义请求头Object.assign(config.headers, this.customHeaders)// 登录请求不需要带 trace-idif (config.url?.includes('/api/admin/login')) {return config}// 从本地缓存获取 Trace-Idconst traceId = localStorage.getItem('trace-id')if (traceId) {config.headers['trace-id'] = traceId}return config},(error) => {ElMessage.error(error)return Promise.reject(error)})// 响应拦截器this.instance.interceptors.response.use((response: AxiosResponse<ApiResponse>) => {const apiResponse = response.data;// 保存新的 Trace-Idconst newTraceId = response.headers['trace-id']if (newTraceId) {localStorage.setItem('trace-id', newTraceId)}// 查 API 响应的状态并处理if (!apiResponse.ok) {// 如果需要处理 API 错误,以在这里加入逻辑ElMessage.error(response.data.err?.msg || '操作失败')return Promise.reject(apiResponse.err);}// 返回原始的 AxiosResponse 类型,包裹 apiResponsereturn {...response,data: apiResponse, // 保持 data 字段为 ApiResponse 类型};},async (error) => {// 即使请求失败也要保存 Trace-Idconst newTraceId = error.response?.headers?.['trace-id']if (newTraceId) {localStorage.setItem('trace-id', newTraceId)}const { response } = error;if (response?.status === 401) {return this.handle401Error(error);}console.error('请求错误:', response?.data || error.message);return Promise.reject(error);});}// 注册特殊错误码的处理器public registerCodeHandler(code: number, handler: CodeHandler) {this.codeHandlers[code] = handler}// 处理 401 错误private async handle401Error(originalError: any) {if (!this.isRefreshing) {this.isRefreshing = truetry {// 从 localStorage 取刷新令牌相关信息const userId = parseInt(localStorage.getItem('userId') || '0')const refreshToken = localStorage.getItem('refreshToken')if (!userId || !refreshToken) {throw new Error('刷新令牌信息不存在')}// 调用刷新令牌接口const refreshParams: RefreshParams = {user_id: userId,refresh_token: refreshToken}const res = await loginApi.refreshToken(refreshParams) as HttpResponse<RefreshData>if (res.data.ok) {// 更新存储的令牌const { token, refresh_token } = res.data.datalocalStorage.setItem('token', token)localStorage.setItem('refreshToken', refresh_token)// 通知所有等待的请求this.refreshSubscribers.forEach(callback => callback(token))this.refreshSubscribers = []// 重试原始请求return this.retryOriginalRequest(originalError, token)} else {throw new Error(res.data.err?.msg || '刷新令牌失败')}} catch (error) {// 清除所有认证信息localStorage.removeItem('token')localStorage.removeItem('refreshToken')localStorage.removeItem('userId')// 显示错误消息ElMessage.error('登录已过期,请重新登录')// 跳转到登录页window.location.href = '/login';return Promise.reject(error)} finally {this.isRefreshing = false}} else {// 如果已经在刷新中,将请求加入队列return new Promise((resolve) => {this.refreshSubscribers.push((token: string) => {resolve(this.retryOriginalRequest(originalError, token))})})}}// 通用重试机制private async retryRequest(config: AxiosRequestConfig, retryCount = 0): Promise<any> {try {return await this.instance.request(config)} catch (error: any) {// 判断是否可以重试const canRetry = this.retryConfig.retryAbleStatus.includes(error.response?.status)&& retryCount < this.retryConfig.maxRetries;if (canRetry) {// 计算延迟时间const delay = this.retryConfig.retryDelay * Math.pow(2, retryCount)console.log(`请求失败,${delay}ms 后进行第 ${retryCount + 1} 次重试`)// 等待延迟时间await new Promise(resolve => setTimeout(resolve, delay))// 递归重试return this.retryRequest(config, retryCount + 1)}// 不能重试则抛出错误throw error}}// token 刷新重试机制private retryOriginalRequest(error: any, token: string) {const config = error.configconfig.headers.Authorization = `${token}`return this.instance.request(config)}public get<T>(url: string, params?: Record<string, any>, config?: AxiosRequestConfig): Promise<T> {return this.instance.get(url, { params, ...config })}// POST 请求public post<T>(url: string, data?: Record<string, any>, config?: AxiosRequestConfig): Promise<T> {return this.instance.post(url, data, config)}// PUT 请求public put<T>(url: string, data?: Record<string, any>, config?: AxiosRequestConfig): Promise<T> {return this.instance.put(url, data, config)}// DELETE 请求public delete<T>(url: string, config?: AxiosRequestConfig): Promise<T> {return this.instance.delete(url, config)}// PATCH 请求public patch<T>(url: string, data?: Record<string, any>, config?: AxiosRequestConfig): Promise<T> {return this.instance.patch(url, data, config)}
}export default HttpClient

api.ts 统一设置请求头

import HttpClient from './http'
// 根据环境变量设置基础 URL
const baseURL = import.meta.env.VITE_API_URLconst apiClient = new HttpClient({baseURL,timeout: 15000,  // 添加超时设置headers: {      // 添加通用请求头'Content-Type': 'application/json'},
})// 添加设置请求头的方法
export const setApiHeaders = (tenantId: string | number, hotelId: string | number) => {// 保存到本地存储localStorage.setItem('tenant-id', tenantId.toString());localStorage.setItem('hotel-id', hotelId.toString());// 设置到请求头apiClient.setCustomHeaders({'tenant-id': tenantId,'hotel-id': hotelId});
}export { apiClient }

http://www.ppmy.cn/news/1576203.html

相关文章

【银河麒麟高级服务器操作系统】服务器测试业务耗时问题分析及处理全流程分享

更多银河麒麟操作系统产品及技术讨论&#xff0c;欢迎加入银河麒麟操作系统官方论坛 https://forum.kylinos.cn 了解更多银河麒麟操作系统全新产品&#xff0c;请点击访问 麒麟软件产品专区&#xff1a;https://product.kylinos.cn 开发者专区&#xff1a;https://developer…

学习笔记-单片机蓝桥杯大模板更新-米醋

一&#xff0c;Seg 底层&#xff1a; 高电平灭&#xff0c;共阳极 ‘,’的ASCII码是44 不消隐的话&#xff1a; 显示完3&#xff0c;想显示5&#xff1a;显示5时有视觉残留 消隐&#xff1a; 显示完3&#xff0c;想显示5&#xff1a;流程&#xff1a;先显示3&#xff0c…

C++ Qt使用命令自动添加exe依赖的Qt库

进入exe文件夹下&#xff0c;打开对应Qt版本的Cmd工具&#xff1a;QT 5.12.2cmd命令工具 执行以下命令&#xff0c;进入exe文件目录&#xff1a; cd E:\proj\bin 如果cd 没有反应&#xff0c;可按顺序执行以下代码进入指定目录&#xff1a; cd \ cd E: cd E:\proj\bin …

深度探索:DeepSeek与鸿蒙HarmonyOS应用开发的深度融合

文章目录 一、概述1.1 什么是DeepSeek&#xff1f;1.2 鸿蒙HarmonyOS的特点 二、技术优势与应用场景2.1 技术优势2.2 应用场景 三、开发指南3.1 环境搭建3.2 集成AI模型3.3 分布式任务调度 四、实际案例分析4.1 智能家居控制4.2 智能健康监测 五、未来展望《AI智能化办公&#…

FFmpeg入门:最简单的音频播放器

FFmpeg入门&#xff1a;最简单的音频播放器 欢迎大家来到FFmpeg入门的第二章&#xff0c;今天只做一个最简单的FFmpeg音频播放器&#xff1b;同样&#xff0c;话不多说&#xff0c;先上流程图 流程图 以上流程和视频播放器的解码过程基本上是一致的&#xff1b; 不同点在于 S…

DeepSeek本地部署与Dify结合创建私有知识库指南

python调用本地deepseek+Dify的API使用--测试WX自动发送信息-CSDN博客 DeepSeek,一家在人工智能领域具有显著技术实力的公司,凭借其千亿参数规模的AI大模型,以及仅需0.5元人民币即可进行百万tokens的API调用成本,已经取得了令人瞩目的成就。不仅如此,DeepSeek的模…

【AI大模型】Ollama部署本地大模型DeepSeek-R1,交互界面Open-WebUI,RagFlow构建私有知识库

文章目录 DeepSeek介绍公司背景核心技术产品与服务应用场景优势与特点访问与体验各个DeepSeek-R系列模型的硬件需求和适用场景 Ollama主要特点优势应用场景安装和使用配置环境变量总结 安装open-webui下载和安装docker desktop配置镜像源安装open-webui运行和使用 RagFlow介绍主…

Tomcat10下载安装教程

一、下载Tomcat10 Tomcat官方网站&#xff1a;Apache Tomcat? - Welcome!左侧Download部分可以下载对应版本的Tomcat。我们以Tomcat10为例注意&#xff1a;Tomcat8、Tomcat9&#xff08;javax.*&#xff09;&#xff1b;Tomcat10&#xff08;jakarta.*&#xff09;点击Downlo…