shadcn-admin与后端API对接:Axios请求封装指南
为什么需要封装Axios?
在现代前端开发中,直接使用Axios原生API会导致大量重复代码和不一致的错误处理。以shadcn-admin项目为例,未封装前可能存在以下痛点:
痛点场景 | 传统解决方案 | 封装后优势 |
---|---|---|
每个请求重复配置baseURL | 硬编码或全局变量 | 集中配置,环境切换更灵活 |
身份验证token管理 | 手动添加Authorization头 | 请求拦截器自动注入 |
网络错误统一处理 | 每个请求单独try/catch | 响应拦截器集中捕获 |
加载状态管理 | 组件内维护loading状态 | 结合React Query自动管理 |
TypeScript类型支持 | 手动定义接口返回类型 | 泛型封装实现类型推导 |
封装前的技术栈准备
shadcn-admin项目已集成以下关键依赖,为Axios封装提供基础:
// package.json 关键依赖片段
{
"dependencies": {
"axios": "^1.11.0",
"@tanstack/react-query": "^5.85.3",
"zod": "^4.0.17",
"react-hook-form": "^7.62.0"
}
}
从零开始的Axios封装实现
1. 创建API基础配置
在src/lib/api.ts
中创建基础Axios实例,集中管理请求配置:
// src/lib/api.ts
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
import { getAccessToken } from '@/stores/auth-store'; // 假设存在的token管理
import { toast } from 'sonner'; // shadcn-admin已集成的toast组件
// 创建基础实例
const api: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_URL || 'https://api.example.com',
timeout: 15000,
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
}
});
// 请求拦截器:注入认证信息和公共参数
api.interceptors.request.use(
(config: AxiosRequestConfig) => {
const token = getAccessToken();
if (token && config.headers) {
config.headers.Authorization = `Bearer ${token}`;
}
// 添加应用版本信息
if (config.params) {
config.params['app_version'] = '2.1.0'; // 取自package.json版本
} else {
config.params = { app_version: '2.1.0' };
}
return config;
},
(error) => Promise.reject(error)
);
2. 响应处理与错误统一拦截
扩展上述文件,添加响应拦截器处理成功/错误场景:
// 响应拦截器:处理成功响应和错误
api.interceptors.response.use(
(response: AxiosResponse) => {
// 假设后端统一返回格式:{ code: number, data: any, message: string }
const { code, data, message } = response.data;
// 业务逻辑错误(非2xx状态码已由Axios默认处理)
if (code !== 200) {
toast.error(message || '操作失败,请重试');
return Promise.reject(new Error(message || '未知错误'));
}
return data; // 直接返回data字段,简化组件中获取数据
},
(error) => {
const { response } = error;
// 分类处理不同错误类型
switch (response?.status) {
case 401:
toast.error('登录状态已过期,请重新登录');
// 触发全局登出逻辑,可结合项目中的auth-store实现
// logout();
break;
case 403:
toast.error('没有权限执行此操作');
break;
case 404:
toast.error('请求的资源不存在');
break;
case 500:
toast.error('服务器内部错误,请稍后重试');
break;
default:
toast.error('网络请求失败,请检查网络连接');
}
return Promise.reject(error);
}
);
3. 类型安全的请求方法封装
创建类型化的请求方法,结合Zod实现请求参数和响应数据的校验:
// src/lib/api.ts 继续添加
import { z } from 'zod';
// 定义基础响应类型
type ApiResponse<T> = {
code: number;
data: T;
message: string;
};
// GET请求封装
export const get = async <T>(
url: string,
config?: AxiosRequestConfig
): Promise<T> => {
return api.get<T>(url, config);
};
// POST请求封装(带Zod校验)
export const post = async <T>(
url: string,
data?: any,
schema?: z.ZodSchema<T>,
config?: AxiosRequestConfig
): Promise<T> => {
// 请求参数校验
if (schema && data) {
const validatedData = schema.parse(data);
return api.post<T>(url, validatedData, config);
}
return api.post<T>(url, data, config);
};
// 导出常用HTTP方法
export const http = {
get,
post,
put: (url: string, data?: any, config?: AxiosRequestConfig) =>
api.put(url, data, config),
delete: (url: string, config?: AxiosRequestConfig) =>
api.delete(url, config),
patch: (url: string, data?: any, config?: AxiosRequestConfig) =>
api.patch(url, data, config)
};
结合React Query实现数据获取
shadcn-admin已集成@tanstack/react-query
,可与Axios封装结合实现高效数据管理:
// src/features/tasks/data/tasks.ts
import { http } from '@/lib/api';
import { z } from 'zod';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
// 定义任务类型Schema
const TaskSchema = z.object({
id: z.string().uuid(),
title: z.string().min(1, '标题不能为空'),
completed: z.boolean(),
createdAt: z.string().datetime()
});
export type Task = z.infer<typeof TaskSchema>;
// 获取任务列表
export const useTasks = () => {
return useQuery({
queryKey: ['tasks'],
queryFn: () => http.get<Task[]>('/api/tasks')
});
};
// 创建任务(带参数校验)
export const useCreateTask = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (taskData: Omit<Task, 'id' | 'createdAt'>) =>
http.post<Task>('/api/tasks', taskData, TaskSchema.omit({ id: true, createdAt: true })),
onSuccess: () => {
// 创建成功后刷新任务列表
queryClient.invalidateQueries({ queryKey: ['tasks'] });
}
});
};
高级功能实现
请求取消与防重复提交
实现请求取消和防抖功能,避免重复请求:
// src/lib/api.ts 添加取消请求功能
import { CancelTokenSource } from 'axios';
// 存储请求取消令牌
const cancelTokens = new Map<string, CancelTokenSource>();
// 带取消功能的GET请求
export const getWithCancel = async <T>(
url: string,
key: string, // 请求唯一标识
config?: AxiosRequestConfig
): Promise<T> => {
// 取消相同key的未完成请求
if (cancelTokens.has(key)) {
cancelTokens.get(key)?.cancel('Request canceled due to new request');
cancelTokens.delete(key);
}
const source = axios.CancelToken.source();
cancelTokens.set(key, source);
try {
return api.get<T>(url, {
...config,
cancelToken: source.token
});
} finally {
cancelTokens.delete(key);
}
};
// 防抖提交实现(使用lodash.debounce)
import { debounce } from 'lodash';
export const debouncedPost = debounce(async <T>(
url: string,
data: any,
delay = 500
): Promise<T> => {
return api.post<T>(url, data);
}, delay);
业务场景实战:用户管理API
// src/features/users/data/users.ts
import { http } from '@/lib/api';
import { z } from 'zod';
// 用户列表请求参数Schema
const UserListParamsSchema = z.object({
page: z.number().int().min(1).default(1),
limit: z.number().int().min(10).max(100).default(20),
keyword: z.string().optional(),
status: z.enum(['active', 'inactive', 'banned']).optional()
});
// 获取用户列表
export const getUsers = async (params: z.infer<typeof UserListParamsSchema>) => {
const validatedParams = UserListParamsSchema.parse(params);
return http.get('/api/users', { params: validatedParams });
};
// 创建用户
export const createUser = async (userData: any) => {
const UserSchema = z.object({
username: z.string().min(3).max(20),
email: z.string().email(),
role: z.enum(['admin', 'editor', 'viewer'])
});
return http.post('/api/users', userData, UserSchema);
};
封装后的项目目录结构
src/
├── lib/
│ ├── api.ts # Axios核心封装
│ ├── utils.ts # 辅助工具函数
│ └── error-handler.ts # 错误处理扩展
├── features/
│ ├── tasks/
│ │ └── data/
│ │ └── tasks.ts # 任务相关API
│ └── users/
│ └── data/
│ └── users.ts # 用户相关API
└── hooks/
└── use-api.ts # 自定义API Hook
最佳实践与注意事项
- 环境变量配置
// .env.development
VITE_API_URL=http://localhost:3000/api
VITE_API_TIMEOUT=15000
// .env.production
VITE_API_URL=https://api.shadcn-admin.com/api
- 请求性能优化
- 使用React Query的缓存功能减少重复请求
- 实现请求合并,避免短时间内相同请求
- 大文件上传使用分片上传:
src/features/uploads/data/upload.ts
- 安全考虑
- 敏感接口添加请求签名:
src/lib/api-sign.ts
- 实现API请求限流:
src/lib/rate-limit.ts
总结与扩展
Axios封装是shadcn-admin项目与后端对接的基础,通过本文介绍的方法,你已经掌握:
- 基础Axios实例配置与拦截器实现
- 类型安全的请求方法封装
- 结合React Query的数据管理方案
- 高级功能:请求取消、防抖、参数校验
后续扩展方向:
- 实现API请求日志系统
- 添加请求性能监控
- 对接WebSocket实时通信
- 集成GraphQL客户端
通过统一的API请求层,shadcn-admin项目可以实现更高效的前后端协作,减少重复代码,提升错误处理能力,为大型管理系统开发提供坚实基础。
收藏本文,关注shadcn-admin项目更新,下期将带来《React Query高级数据管理实战》。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考