Axios多实例封装方案
我将为您提供一个完整的Axios多实例封装方案,包含基础封装、多实例管理和使用示例。
设计思路
- 创建基础axios实例封装,支持请求/响应拦截器
- 实现多实例管理器,支持不同API端点配置
- 提供统一的错误处理机制
- 支持请求取消功能
- 提供完整的类型定义(TypeScript)
实现代码
1. 基础封装和类型定义
typescript
// types.ts
export interface RequestConfig {
url: string;
method?: 'get' | 'post' | 'put' | 'delete' | 'patch';
data?: any;
params?: any;
headers?: Record<string, string>;
timeout?: number;
withCredentials?: boolean;
}
export interface ResponseData<T = any> {
data: T;
status: number;
statusText: string;
headers: any;
config: RequestConfig;
}
export interface ApiError extends Error {
config: RequestConfig;
code?: string;
request?: any;
response?: ResponseData;
}
export interface ApiInstance {
request: <T = any>(config: RequestConfig) => Promise<ResponseData<T>>;
get: <T = any>(url: string, params?: any, config?: Omit<RequestConfig, 'url' | 'method' | 'data'>) => Promise<ResponseData<T>>;
post: <T = any>(url: string, data?: any, config?: Omit<RequestConfig, 'url' | 'method' | 'params'>) => Promise<ResponseData<T>>;
put: <T = any>(url: string, data?: any, config?: Omit<RequestConfig, 'url' | 'method' | 'params'>) => Promise<ResponseData<T>>;
delete: <T = any>(url: string, params?: any, config?: Omit<RequestConfig, 'url' | 'method' | 'data'>) => Promise<ResponseData<T>>;
patch: <T = any>(url: string, data?: any, config?: Omit<RequestConfig, 'url' | 'method' | 'params'>) => Promise<ResponseData<T>>;
}
export interface ApiInstanceConfig {
baseURL?: string;
timeout?: number;
headers?: Record<string, string>;
withCredentials?: boolean;
transformRequest?: (config: RequestConfig) => RequestConfig;
transformResponse?: (response: ResponseData) => ResponseData;
requestInterceptors?: Array<(config: RequestConfig) => RequestConfig | Promise<RequestConfig>>;
responseInterceptors?: Array<(response: ResponseData) => ResponseData | Promise<ResponseData>>;
errorInterceptors?: Array<(error: ApiError) => Promise<never>>;
}
2. 核心封装类
typescript
// axios-instance.ts
import axios, { AxiosRequestConfig, AxiosResponse, AxiosError, CancelTokenSource } from 'axios';
import {
RequestConfig,
ResponseData,
ApiError,
ApiInstance,
ApiInstanceConfig
} from './types';
export class AxiosInstance implements ApiInstance {
private instance: any;
private cancelTokenSource: CancelTokenSource;
constructor(config: ApiInstanceConfig = {}) {
this.cancelTokenSource = axios.CancelToken.source();
const axiosConfig: AxiosRequestConfig = {
baseURL: config.baseURL,
timeout: config.timeout || 30000,
headers: {
'Content-Type': 'application/json',
...config.headers
},
withCredentials: config.withCredentials || false,
cancelToken: this.cancelTokenSource.token
};
this.instance = axios.create(axiosConfig);
// 请求拦截器
this.instance.interceptors.request.use(
(axiosConfig: AxiosRequestConfig) => {
let finalConfig: RequestConfig = {
url: axiosConfig.url || '',
method: axiosConfig.method as any,
data: axiosConfig.data,
params: axiosConfig.params,
headers: axiosConfig.headers,
timeout: axiosConfig.timeout,
withCredentials: axiosConfig.withCredentials
};
// 应用自定义请求转换
if (config.transformRequest) {
finalConfig = config.transformRequest(finalConfig);
}
// 应用请求拦截器
if (config.requestInterceptors) {
for (const interceptor of config.requestInterceptors) {
finalConfig = interceptor(finalConfig);
}
}
return {
...axiosConfig,
...finalConfig
};
},
(error: AxiosError) => {
const apiError: ApiError = {
name: 'RequestError',
message: error.message,
config: error.config as RequestConfig,
code: error.code,
request: error.request
};
return Promise.reject(apiError);
}
);
// 响应拦截器
this.instance.interceptors.response.use(
(response: AxiosResponse) => {
const responseData: ResponseData = {
data: response.data,
status: response.status,
statusText: response.statusText,
headers: response.headers,
config: response.config as RequestConfig
};
let finalResponse = responseData;
// 应用响应拦截器
if (config.responseInterceptors) {
for (const interceptor of config.responseInterceptors) {
finalResponse = interceptor(finalResponse);
}
}
// 应用自定义响应转换
if (config.transformResponse) {
finalResponse = config.transformResponse(finalResponse);
}
return finalResponse;
},
(error: AxiosError) => {
const apiError: ApiError = {
name: 'ResponseError',
message: error.message,
config: error.config as RequestConfig,
code: error.code,
request: error.request,
response: error.response ? {
data: error.response.data,
status: error.response.status,
statusText: error.response.statusText,
headers: error.response.headers,
config: error.response.config as RequestConfig
} : undefined
};
// 应用错误拦截器
if (config.errorInterceptors) {
for (const interceptor of config.errorInterceptors) {
return interceptor(apiError);
}
}
return Promise.reject(apiError);
}
);
}
async request<T = any>(config: RequestConfig): Promise<ResponseData<T>> {
try {
return await this.instance.request(config);
} catch (error) {
throw error as ApiError;
}
}
get<T = any>(url: string, params?: any, config?: Omit<RequestConfig, 'url' | 'method' | 'data'>): Promise<ResponseData<T>> {
return this.request<T>({
url,
method: 'get',
params,
...config
});
}
post<T = any>(url: string, data?: any, config?: Omit<RequestConfig, 'url' | 'method' | 'params'>): Promise<ResponseData<T>> {
return this.request<T>({
url,
method: 'post',
data,
...config
});
}
put<T = any>(url: string, data?: any, config?: Omit<RequestConfig, 'url' | 'method' | 'params'>): Promise<ResponseData<T>> {
return this.request<T>({
url,
method: 'put',
data,
...config
});
}
delete<T = any>(url: string, params?: any, config?: Omit<RequestConfig, 'url' | 'method' | 'data'>): Promise<ResponseData<T>> {
return this.request<T>({
url,
method: 'delete',
params,
...config
});
}
patch<T = any>(url: string, data?: any, config?: Omit<RequestConfig, 'url' | 'method' | 'params'>): Promise<ResponseData<T>> {
return this.request<T>({
url,
method: 'patch',
data,
...config
});
}
// 取消当前实例的所有请求
cancelAllRequests(message?: string): void {
this.cancelTokenSource.cancel(message || 'Request canceled');
// 创建新的cancel token用于后续请求
this.cancelTokenSource = axios.CancelToken.source();
this.instance.defaults.cancelToken = this.cancelTokenSource.token;
}
// 获取当前cancel token,可用于特定请求的取消
getCancelToken() {
return this.cancelTokenSource.token;
}
}
3. 多实例管理器
typescript
// api-manager.ts
import { AxiosInstance } from './axios-instance';
import { ApiInstanceConfig, ApiInstance } from './types';
export class ApiManager {
private instances: Map<string, AxiosInstance> = new Map();
private static instance: ApiManager;
private constructor() {}
static getInstance(): ApiManager {
if (!ApiManager.instance) {
ApiManager.instance = new ApiManager();
}
return ApiManager.instance;
}
createInstance(name: string, config: ApiInstanceConfig = {}): AxiosInstance {
if (this.instances.has(name)) {
throw new Error(`API instance with name "${name}" already exists`);
}
const instance = new AxiosInstance(config);
this.instances.set(name, instance);
return instance;
}
getInstance(name: string): AxiosInstance {
const instance = this.instances.get(name);
if (!instance) {
throw new Error(`API instance with name "${name}" not found`);
}
return instance;
}
removeInstance(name: string): void {
this.instances.delete(name);
}
hasInstance(name: string): boolean {
return this.instances.has(name);
}
getAllInstanceNames(): string[] {
return Array.from(this.instances.keys());
}
// 取消所有实例的所有请求
cancelAllRequests(message?: string): void {
this.instances.forEach(instance => {
instance.cancelAllRequests(message);
});
}
}
// 导出单例
export const apiManager = ApiManager.getInstance();
4. 预设实例配置
typescript
// api-configs.ts
import { ApiInstanceConfig } from './types';
// 认证拦截器
const authRequestInterceptor = (config: any) => {
const token = localStorage.getItem('auth_token');
if (token) {
config.headers = config.headers || {};
config.headers.Authorization = `Bearer ${token}`;
}
return config;
};
// 通用响应拦截器 - 处理标准响应格式
const standardResponseInterceptor = (response: any) => {
// 假设后端返回格式为 { code: number, data: any, message: string }
if (response.data && typeof response.data === 'object') {
if (response.data.code !== 0 && response.data.code !== 200) {
throw new Error(response.data.message || 'Request failed');
}
// 返回数据部分
return {
...response,
data: response.data.data
};
}
return response;
};
// 错误处理拦截器
const errorInterceptor = (error: any) => {
if (error.response) {
switch (error.response.status) {
case 401:
// 处理未授权
localStorage.removeItem('auth_token');
window.location.href = '/login';
break;
case 403:
// 处理禁止访问
console.error('Permission denied');
break;
case 500:
// 处理服务器错误
console.error('Server error');
break;
default:
console.error('Request error', error.message);
}
} else if (error.request) {
console.error('Network error', error.message);
} else {
console.error('Request setup error', error.message);
}
return Promise.reject(error);
};
// 主API配置
export const mainApiConfig: ApiInstanceConfig = {
baseURL: process.env.REACT_APP_API_BASE_URL || 'https://api.example.com',
timeout: 30000,
headers: {
'X-Requested-With': 'XMLHttpRequest'
},
requestInterceptors: [authRequestInterceptor],
responseInterceptors: [standardResponseInterceptor],
errorInterceptors: [errorInterceptor]
};
// 认证API配置
export const authApiConfig: ApiInstanceConfig = {
baseURL: process.env.REACT_APP_AUTH_API_URL || 'https://auth.example.com',
timeout: 15000,
headers: {
'X-API-Source': 'web-frontend'
},
responseInterceptors: [standardResponseInterceptor],
errorInterceptors: [errorInterceptor]
};
// 第三方API配置
export const thirdPartyApiConfig: ApiInstanceConfig = {
baseURL: 'https://api.thirdparty.com',
timeout: 20000,
transformResponse: (response) => {
// 转换第三方API的响应格式
if (response.data && response.data.result) {
return {
...response,
data: response.data.result
};
}
return response;
}
};
5. 使用示例
typescript
// api-instances.ts
import { apiManager } from './api-manager';
import { mainApiConfig, authApiConfig, thirdPartyApiConfig } from './api-configs';
// 创建多个API实例
export const mainApi = apiManager.createInstance('main', mainApiConfig);
export const authApi = apiManager.createInstance('auth', authApiConfig);
export const thirdPartyApi = apiManager.createInstance('thirdParty', thirdPartyApiConfig);
// 业务API函数
export const userApi = {
// 获取用户信息
getUser: (userId: number) => mainApi.get(`/users/${userId}`),
// 更新用户信息
updateUser: (userId: number, data: any) => mainApi.put(`/users/${userId}`, data),
// 获取用户列表
getUsers: (params?: { page: number; limit: number }) => mainApi.get('/users', params)
};
export const authService = {
// 登录
login: (credentials: { email: string; password: string }) =>
authApi.post('/login', credentials),
// 注册
register: (userData: any) => authApi.post('/register', userData),
// 退出登录
logout: () => authApi.post('/logout')
};
export const thirdPartyService = {
// 获取第三方数据
getData: (id: string) => thirdPartyApi.get(`/data/${id}`),
// 提交数据到第三方
submitData: (data: any) => thirdPartyApi.post('/submit', data)
};
// 取消所有请求(例如在路由切换时)
export const cancelAllRequests = (message?: string) => {
apiManager.cancelAllRequests(message);
};
6. 在React组件中使用
typescript
// UserProfile.tsx
import React, { useState, useEffect } from 'react';
import { userApi, cancelAllRequests } from './api-instances';
const UserProfile: React.FC<{ userId: number }> = ({ userId }) => {
const [user, setUser] = useState<any>(null);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string>('');
useEffect(() => {
const fetchUser = async () => {
try {
setLoading(true);
setError('');
const response = await userApi.getUser(userId);
setUser(response.data);
} catch (err: any) {
setError(err.message || 'Failed to fetch user');
} finally {
setLoading(false);
}
};
fetchUser();
// 组件卸载时取消请求
return () => {
cancelAllRequests('Component unmounted');
};
}, [userId]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return (
<div>
<h1>{user.name}</h1>
<p>Email: {user.email}</p>
{/* 更多用户信息 */}
</div>
);
};
export default UserProfile;
主要特性
- 多实例支持:可以创建多个Axios实例,每个实例有独立的配置
- 类型安全:完整的TypeScript支持
- 拦截器链:支持请求/响应拦截器,支持异步拦截器
- 错误处理:统一的错误处理机制
- 请求取消:支持取消请求,避免内存泄漏
- 响应转换:支持自定义响应数据转换
- 单例管理:通过ApiManager统一管理所有实例
使用建议
- 根据业务模块划分不同的API实例
- 为不同后端服务创建独立的实例
- 在路由切换时取消未完成的请求
- 使用拦截器统一处理认证、错误提示等通用逻辑
- 根据项目需求扩展配置选项
这个封装方案提供了灵活而强大的Axios多实例管理能力,适合中大型前端项目使用。