背景
参考很多文章,基本上使用ts对axios进行类封装的文章很少,所以想着使用ts进行类封装,希望可以给大家一点帮助。本次使用axios的版本为v1.3.3,Github地址
本次封装有如下功能:
- 配置不同AxiosRequestConfig请求不同的接口地址
- 接口失败返回固定数据,不需要进行try...catch捕获
- 通过配置,直接显示成功失败提示
- 单个接口可以进行特定的请求拦截
- 接口重复请求取消功能
目录结构如下
request
├─index.ts // 对外暴露的函数
├─request.ts // 封装类
├─types
| └index.ts // 类型定义
1、项目中通过配置请求多个接口
在一个项目中通常存在一个接口地址,如果存在多个接口地址,通常会将axios的常规封装写两次,进行使用。
1.1 封装一个axios类
类中的的请求和响应拦截,都是使用传入的拦截器,目的是方便多个接口地址进行配置。
// request.ts 文件
import axios from 'axios'
import type { AxiosError, AxiosResponse, AxiosInstance } from 'axios'
import type { RequestConfig, RequestInterceptors } from './types'
class Request {
private instance: AxiosInstance
private interceptorsObj?: RequestInterceptors<AxiosResponse>
constructor(config: RequestConfig) {
// 1.创建axios实例
this.instance = axios.create(config)
// 2.传入拦截器
this.interceptorsObj = config.interceptors
// 3.此处为了多个config传入不同的拦截器
this.instance.interceptors.request.use(
this.interceptorsObj?.requestInterceptors,
this.interceptorsObj?.requestInterceptorsCatch
)
this.instance.interceptors.response.use(
this.interceptorsObj?.responseInterceptors,
this.interceptorsObj?.responseInterceptorsCatch
)
}
public request<T>(config: RequestConfig<T>): Promise<T> {
return new Promise((resolve, reject) => {
// 单个请求设置拦截器在这里
if (config.interceptors?.requestInterceptors) {
this.instance.interceptors.request.use(config.interceptors.requestInterceptor)
}
this.instance
.request<any, T>(config)
.then(res => {
// 接口数据响应处理
if (config.interceptors?.responseInterceptors) {
res = config.interceptors.responseInterceptors(res)
}
resolve(res)
})
.catch((err: AxiosError) => {
// 向上抛出错误
reject(err)
})
})
}
}
export default Request
1.2 类型定义:
定义好基础的拦截器后,我们需要改造我们传入的参数的类型,因为axios提供的AxiosRequestConfig
是不允许我们传入拦截器的,所以说我们自定义了RequestConfig
,让其继承与AxiosRequestConfig
。
import type { AxiosError, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios'
export interface RequestInterceptors<T> {
//请求拦截
requestInterceptors?: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig
requestInterceptorsCatch?: (err: AxiosError) => any
//响应拦截
responseInterceptors?: (response: T) => T
responseInterceptorsCatch?: (err: AxiosError) => any
}
export interface RequestConfig<T = AxiosResponse> extends AxiosRequestConfig {
interceptors?: RequestInterceptors<T>
// 用于接口成功失败的提示文案
desc?: string
}
// 返回参数定义
export interface IBaseRes<T = any> {
code: number
data: T
msg: string
err: boolean
}
// 将请求包一层 转换成 请求 响应的方式
export interface IReq<P, T> extends RequestConfig<IBaseRes<T>> {
data?: P
// 添加属性 默认post,delete,put返回
desc?: string
}
// 将可选的部分参数转换成必填
export type CustomRequired<T, K extends keyof T> = {
[P in K]-?: T[P]
} & Omit<T, K>
1.3 对外暴露函数
使用高阶函数,第一层传递接口基本信息,第二层接受具体接口的入参和出参。错误时,返回code为0的响应数据,这样即使接口错误,不需要进行try...catch。只需要判断err是否为true
// index.ts
import Request from './request'
import type { IBaseRes, RequestConfig, IReq } from './types'
export default (config: RequestConfig) => {
const api = new Request(config)
return function <K = any, T = any>(config: IReq<K, T>): Promise<IBaseRes<T>> {
return new Promise((resolve, reject) => {
const { method = 'GET' } = config
if (method === 'get' || method === 'GET') {
config.params = config.data
delete config.data
}
return api
.request<IBaseRes<T>>(config)
.then(res => {
resolve({ ...res, err: false })
})
.catch(err => {
resolve({ code: 0, data: null as T, msg: 'fail', err: true })
})
})
}
}
1.4 使用
通过config 文件配置不同的接口地址,导出接口函数。
在全局的响应拦截中,显示成功失败提示。 可以添加接口重复请求取消功能,参考接口取消功能
// config.ts
import type { RequestConfig, CustomRequired, IBaseRes } from '../request/types'
import type { AxiosResponse, AxiosError } from 'axios'
import setApiConfig from '../request'
import abort from '@sunrisecn/axios-abort'
import { notification } from 'ant-design-vue'
const config: CustomRequired<RequestConfig, 'baseURL'> = {
baseURL: 'http://localhost:3000',
timeout: 1000 * 60 * 5,
interceptors: {
requestInterceptors: config => {
config.signal = abort.add({ url: config.url!, method: config.method! })
abort.judge({ url: config.url!, method: config.method! })
return config
},
requestInterceptorsCatch: error => {
return Promise.reject(error)
},
responseInterceptors: response => {
// 添加提示
notify.onFulfilled(response)
abort.remove({ url: response.config.url!, method: response.config.method! })
return response
},
responseInterceptorsCatch: error => {
// 错误提示
notify.onRejected(error)
return Promise.reject(error)
}
}
}
const config1: CustomRequired<RequestConfig, 'baseURL'> = {
baseURL: 'http://localhost:8000',
timeout: 1000 * 60 * 5,
interceptors: {
requestInterceptors: config => {
return config
},
requestInterceptorsCatch: error => {
return Promise.reject(error)
},
responseInterceptors: response => {
return response
},
responseInterceptorsCatch: error => {
return Promise.reject(error)
}
}
}
// 接口成功或失败的提示
const notify = {
onFulfilled: (response: AxiosResponse<IBaseRes>) => {
const { code, msg } = response.data
const { desc, method } = response.config as RequestConfig
// 如果desc被定义,则执行反馈逻辑
// 判断code码 1是成功
if (code === 1 && desc) {
// 判断是否需要显示
notification.success({
message: `${desc}成功`
})
// 是 0 且需要失败提醒 才展示
} else if (code === 0) {
notification.error({
message: `${desc}错误`,
description: `原因:${msg}`
})
}
},
onRejected: (error: AxiosError) => {
const { response, config } = error
// 对4xx,5xx状态码做失败反馈
const { url, desc } = config as RequestConfig
if (!desc) {
return Promise.reject(error)
}
if (response?.status && response?.statusText) {
notification.error({
message: `${desc}错误`,
description: `状态:${response.status}~${response.statusText}
路径:${url}`
})
} else {
// 处理请求响应失败,例如网络offline,超时等做失败反馈
notification.error({
message: `${desc}失败`,
description: `原因:${error.message}路径:${url}`
})
}
}
}
export const request = setApiConfig(config)
export const request1 = setApiConfig(config1)
api文件中使用,此处可以进行配置单个接口的请求和响应拦截。
import { request } from './config'
interface IP {
name: string
age: number
}
export const getList = (data: IP) =>
request<IP, number>({
url: '/getList',
method: 'get',
data,
desc: '获取列表成功',
interceptors: {
requestInterceptors: config => {
console.log('打印***单个接口请求拦截', config)
return config
},
responseInterceptors: res => {
if (res.data) {
console.log('打印***单个接口响应拦截', res)
}
return res
}
}
})
参考文章: