shadcn-admin与后端API对接:Axios请求封装指南

shadcn-admin与后端API对接:Axios请求封装指南

【免费下载链接】shadcn-admin Admin Dashboard UI built with Shadcn and Vite. 【免费下载链接】shadcn-admin 项目地址: https://gitcode.com/GitHub_Trending/sh/shadcn-admin

为什么需要封装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

最佳实践与注意事项

  1. 环境变量配置
// .env.development
VITE_API_URL=http://localhost:3000/api
VITE_API_TIMEOUT=15000

// .env.production
VITE_API_URL=https://api.shadcn-admin.com/api
  1. 请求性能优化
  • 使用React Query的缓存功能减少重复请求
  • 实现请求合并,避免短时间内相同请求
  • 大文件上传使用分片上传:src/features/uploads/data/upload.ts
  1. 安全考虑
  • 敏感接口添加请求签名: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高级数据管理实战》。

【免费下载链接】shadcn-admin Admin Dashboard UI built with Shadcn and Vite. 【免费下载链接】shadcn-admin 项目地址: https://gitcode.com/GitHub_Trending/sh/shadcn-admin

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值