记录一下最近项目使用的axios封装方法
完整代码
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError, InternalAxiosRequestConfig } from 'axios';
interface ApiResponse<T> {
message: string;
data: T;
success: boolean;
}
const NoAuthUrl = ['/users/login', '/users/otp/verify']
// 处理401错误的函数
function handle401Error(): void {
// 只在浏览器环境中执行
if (typeof window !== "undefined") {
// 获取当前路径
const currentPath = window.location.pathname;
// 排除登录和注册页面
const excludePaths = ["/login", "/register"];
if (!excludePaths.includes(currentPath)) {
// 清除认证token
localStorage.removeItem("auth_token");
// 重定向到登录页面
window.location.href = "/login";
}
}
}
// 请求状态管理
interface RequestState {
[key: string]: boolean;
}
// 全局loading状态
const loadingState: RequestState = {};
// 创建自定义axios实例
const apiClient: AxiosInstance = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL || "",
timeout: 10000,
headers: {
"Content-Type": "application/json",
},
});
// 请求拦截器
apiClient.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
// 生成请求唯一标识
const requestId = generateRequestId(config);
// 设置loading状态
loadingState[requestId] = true;
// 添加认证token
const token =
typeof window !== "undefined" ? localStorage.getItem("auth_token") : null;
if (token && !NoAuthUrl.includes(config.url || '')) {
config.headers = config.headers || {};
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error: AxiosError) => {
return Promise.reject(error);
}
);
// 响应拦截器
apiClient.interceptors.response.use(
(response: AxiosResponse) => {
// 清除loading状态
const requestId = generateRequestId(response.config);
loadingState[requestId] = false;
return response.data;
},
(error: AxiosError) => {
// 清除loading状态
if (error.config) {
const requestId = generateRequestId(error.config);
loadingState[requestId] = false;
}
// 错误处理
handleApiError(error);
return Promise.reject(error);
}
);
// 生成请求唯一标识
function generateRequestId(
config: AxiosRequestConfig | InternalAxiosRequestConfig
): string {
const { method, url, params, data } = config;
return `${method}_${url}_${JSON.stringify(params)}_${JSON.stringify(data)}`;
}
// 检查请求是否正在加载
export function isLoading(
config: AxiosRequestConfig | InternalAxiosRequestConfig
): boolean {
const requestId = generateRequestId(config);
return !!loadingState[requestId];
}
// 错误处理函数
function handleApiError(error: AxiosError): void {
let errorMessage = "请求失败,请稍后重试";
if (error.response) {
// 服务器返回错误状态码
const status = error.response.status;
const data = error.response.data as any;
switch (status) {
case 400:
errorMessage = data.error || "请求参数错误";
break;
case 401:
errorMessage = "未授权,请重新登录";
// 处理401错误,排除登录和注册页面
handle401Error();
break;
case 403:
errorMessage = "拒绝访问";
break;
case 404:
errorMessage = data.error || "请求的资源不存在";
break;
case 500:
errorMessage = data.error || "服务器错误";
break;
default:
errorMessage = data.error || `请求失败(${status})`;
}
} else if (error.request) {
// 请求发出但没有收到响应
errorMessage = "网络错误,请检查您的网络连接";
} else {
// 请求配置出错
errorMessage = error.message || "请求配置错误";
}
// 显示错误提示
// 由于flowbite-react不支持直接调用toast,这里只记录到控制台
// 实际使用时可以通过组件方式或其他toast库显示错误
console.error("API请求错误:", errorMessage, error);
}
// 通用请求方法
export async function request<T>(
config: AxiosRequestConfig
): Promise<ApiResponse<T>> {
try {
return (await apiClient(config)) as ApiResponse<T>;
} catch (error) {
throw error;
}
}
// 导出封装的请求方法
export default {
get: <T>(url: string, params?: any, config?: AxiosRequestConfig) =>
request<T>({ ...config, method: 'get', url, params }),
post: <T>(url: string, data?: any, config?: AxiosRequestConfig) =>
request<T>({ ...config, method: 'post', url, data }),
put: <T>(url: string, data?: any, config?: AxiosRequestConfig) =>
request<T>({ ...config, method: 'put', url, data }),
delete: <T>(url: string, config?: AxiosRequestConfig) =>
request<T>({ ...config, method: 'delete', url }),
// 获取当前loading状态
isLoading,
};
实现考虑
1. loading状态
使用请求的method,url,data等值作为唯一id存储,页面想要使用的时候,使用jotai的get或者useMemo等方法获取loading状态,给页面的loading组件使用
2. ApiResponse泛型
定义通用ApiResponse泛型,接口请求只需传入主要data类型
3. auth_token
从localStorage中获取登录获取的token,在request中添加Authorization,同时维护一个url数组,过滤注册和登录等接口,避免出现bug
4. 错误处理
主要针对401进行错误处理,进行重定向