request 封装axios
2026/1/17大约 3 分钟
request 封装axios
使用 axios 来封装请求。
class AxiosRequest {
// 实例
instance: AxiosInstance
// 拦截器对象
interceptors: Undefinable<RequestInterceptors<AxiosResponse>>
// 取消请求控制器
abortControllerMap: Map<string, AbortController>
constructor(config: CreateRequestConfig) {
this.instance = axios.create(config)
this.abortControllerMap = new Map()
this.interceptors = config.interceptors
// 拦截器执行顺序 接口请求 - 实例请求 - 全局请求 - 实例响应 - 全局响应 - 接口响应
this.instance.interceptors.request.use((res: InternalAxiosRequestConfig) => {
const controller = new AbortController()
res.signal = controller.signal
const mapKey = this.generateMapKey(res)
// 保存key到请求配置中,供响应拦截器使用
;(res as NewAxiosRequestConfig)._mapKey = mapKey
// 如果存在则删除该请求
if (this.abortControllerMap.get(mapKey)) {
console.error('取消重复请求:', mapKey)
this.cancelRequest(mapKey)
} else {
this.abortControllerMap.set(mapKey, controller)
}
return res
})
// 使用实例拦截器
this.instance.interceptors.request.use(
this.interceptors?.requestInterceptors,
this.interceptors?.requestInterceptorsCatch,
)
this.instance.interceptors.response.use(
this.interceptors?.responseInterceptors,
this.interceptors?.responseInterceptorsCatch,
)
// 全局响应拦截器保证最后执行
this.instance.interceptors.response.use(
// 因为我们接口的数据都在res.data下,所以我们直接返回res.data
(res: AxiosResponse) => {
// 从请求配置中获取之前保存的key
const mapKey = (res.config as NewAxiosRequestConfig)._mapKey || ''
this.abortControllerMap.delete(mapKey)
return res.data
},
(err: object) => {
return Promise.reject(err)
},
)
}
/**
* 取消指定的请求
* @param url - 待取消的请求URL
*/
cancelRequest(url: string | string[]) {
const urlList = Array.isArray(url) ? url : [url]
for (const _url of urlList) {
this.abortControllerMap.get(_url)?.abort()
this.abortControllerMap.delete(_url)
}
}
/**
* 取消全部请求
*/
cancelAllRequest() {
for (const [, controller] of this.abortControllerMap) {
controller.abort()
}
this.abortControllerMap.clear()
}
/**
* get请求
* @param url - 链接
* @param options - 参数
*/
get<T = object>(url: string, options = {}) {
return this.instance.get(url, options) as Promise<ServerResult<T>>
}
/**
* post请求
* @param url - 链接
* @param options - 参数
* @param config
*/
post<T = object>(url: string, options = {}, config?: AxiosRequestConfig<object>) {
return this.instance.post(url, options, config) as Promise<ServerResult<T>>
}
/**
* put请求
* @param url - 链接
* @param options - 参数
* @param config
*/
put<T = object>(url: string, options = {}, config?: AxiosRequestConfig<object>) {
return this.instance.put(url, options, config) as Promise<ServerResult<T>>
}
/**
* delete请求
* @param url - 链接
* @param options - 参数
*/
delete<T = object>(url: string, options = {}) {
return this.instance.delete(url, options) as Promise<ServerResult<T>>
}
/**
* patch请求
* @param url - 链接
* @param options - 参数
*/
patch<T = object>(url: string, options = {}) {
return this.instance.patch(url, options) as Promise<ServerResult<T>>
}
/**
* 生成请求的唯一key(考虑参数)
*/
private generateMapKey(requestConfig: NewAxiosRequestConfig) {
let url = requestConfig.method || ''
if (requestConfig.url) url += `^${requestConfig.url}`
// 如果存在参数
if (requestConfig.params) {
for (const key in requestConfig.params) {
url += `&${key}=${requestConfig.params[key]}`
}
}
// 如果存在post数据
if (
requestConfig.data &&
requestConfig.data?.[0] === '{' &&
requestConfig.data?.[requestConfig.data?.length - 1] === '}'
) {
const obj = JSON.parse(requestConfig.data)
for (const key in obj) {
url += `#${key}=${obj[key]}`
}
}
return url
}
}创建实例方法
function createRequest(url: string, tokenKey: string) {
return new AxiosRequest({
baseURL: url,
timeout: 180 * 1000,
interceptors: {
// 接口请求拦截
requestInterceptors(res) {
const tokenLocal = getLocalInfo(tokenKey) || ''
if (res?.headers && tokenLocal) {
res.headers.Authorization = `Bearer ${tokenLocal}` as string
}
return res
},
// 请求拦截超时
requestInterceptorsCatch(err) {
message.error('请求超时!')
return err
},
// 接口响应拦截
responseInterceptors(res) {
const {data} = res
// 权限不足
if (data?.code === 401) {
handle401Error(tokenKey)
return res
}
// 错误处理
if (data?.code !== 200) {
handleError(data?.message)
return res
}
return res
},
responseInterceptorsCatch(err) {
if (axios.isCancel(err)) {
// 取消重复请求则不报错
set(err, 'data', {})
return Promise.reject(err)
} else if (axios.isAxiosError(err)) {
if (err.status === 401) {
handle401Error(tokenKey)
}
}
handleError((err as RequestCancel)?.response?.data?.message || '服务器错误!')
return Promise.reject(err)
},
},
})
}错误处理方法
/**
* 异常处理
* @param error - 错误信息
* @param content - 自定义内容
*/
const handleError = (error: string, content?: string) => {
console.error('错误信息:', error)
message.error({
content: content || error || '服务器错误',
key: 'error',
})
}
const handle401Error = (tokenKey: string) => {
const lang = localStorage.getItem('lang')
const enMsg = 'Insufficient permissions, please log in again!'
const zhMsg = '权限不足,请重新登录!'
const msg = lang === 'en' ? enMsg : zhMsg
removeLocalInfo(tokenKey)
message.error({
content: msg,
key: 'error',
})
console.error('错误信息:', msg)
// 跳转登录页
const url = window.location.href
if (url.includes('#')) {
window.location.hash = '/login'
} else {
// window.location.href跳转会出现message无法显示情况,所以需要延时
setTimeout(() => {
window.location.href = '/login'
}, 1000)
}
}