TypeScript网络库axios 2025-06-10 周二

axios 是一个基于 Promise 的 HTTP 客户端,用于浏览器和 Node.js。它具有拦截请求和响应、转换请求和响应数据、取消请求、自动转换 JSON 数据等功能,并且在 TypeScript 中提供了很好的类型支持。

安装

npm install axios
# 或者
yarn add axios

基本用法

  • 发送 GET 请求
import axios from 'axios';

// 定义响应数据的类型
interface User {
  id: number;
  name: string;
  email: string;
}

async function fetchUsers() {
  try {
    // 发送 GET 请求
    const response = await axios.get<User[]>('https://jsonplaceholder.typicode.com/users');
    
    // response.data 会被自动类型推断为 User[]
    const users = response.data;
    
    console.log('First user:', users[0].name);
    return users;
  } catch (error) {
    // 处理错误
    if (axios.isAxiosError(error)) {
      console.error('Axios error:', error.message);
      
      // 获取错误响应数据
      if (error.response) {
        console.error('Status:', error.response.status);
        console.error('Data:', error.response.data);
      }
    } else {
      console.error('Unexpected error:', error);
    }
    
    throw error;
  }
}

// 使用示例
fetchUsers().then(users => console.log('Users:', users));
  • 发送 POST 请求
interface Post {
  id?: number; // id 是可选的,因为创建时可能还没有
  title: string;
  body: string;
  userId: number;
}

async function createPost(postData: Omit<Post, 'id'>) {
  try {
    // 发送 POST 请求
    const response = await axios.post<Post>('https://jsonplaceholder.typicode.com/posts', postData);
    
    // 返回创建的帖子
    return response.data;
  } catch (error) {
    console.error('Error creating post:', error);
    throw error;
  }
}

// 使用示例
createPost({
  title: 'New Post',
  body: 'This is a new post',
  userId: 1
}).then(newPost => console.log('Created post:', newPost));

创建 axios 实例

import axios, { AxiosInstance, AxiosError } from 'axios';

// 创建 axios 实例
const apiClient: AxiosInstance = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 10000, // 请求超时时间
  headers: {
    'Content-Type': 'application/json',
    'X-App-Version': '1.0.0'
  }
});

// 设置认证令牌(如果有)
apiClient.defaults.headers.common['Authorization'] = 'Bearer your_token_here';

// 定义错误处理函数
function handleApiError(error: unknown) {
  if (axios.isAxiosError(error)) {
    const axiosError: AxiosError = error;
    
    if (axiosError.response) {
      // 请求已发送,服务器返回状态码超出了 2xx 范围
      console.error('Response error:', axiosError.response.data);
      console.error('Status code:', axiosError.response.status);
    } else if (axiosError.request) {
      // 请求已发送,但没有收到响应
      console.error('No response received:', axiosError.request);
    } else {
      // 在设置请求时触发错误
      console.error('Error setting up request:', axiosError.message);
    }
  } else {
    // 处理非 axios 错误
    console.error('Unexpected error:', error);
  }
}

// 使用实例发送请求
async function fetchData() {
  try {
    const response = await apiClient.get('/data');
    return response.data;
  } catch (error) {
    handleApiError(error);
    throw error;
  }
}

请求拦截器和响应拦截器

// 请求拦截器:在请求发送前添加认证信息
apiClient.interceptors.request.use(
  (config) => {
    // 从本地存储获取 token
    const token = localStorage.getItem('auth_token');
    
    // 如果 token 存在,则添加到请求头
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    
    return config;
  },
  (error) => {
    // 处理请求错误
    return Promise.reject(error);
  }
);

// 响应拦截器:统一处理响应数据
apiClient.interceptors.response.use(
  (response) => {
    // 可以在这里对响应数据进行预处理
    return response.data; // 直接返回 data,简化后续使用
  },
  (error) => {
    // 统一处理错误
    if (axios.isAxiosError(error)) {
      if (error.response?.status === 401) {
        // 处理未授权情况,例如跳转到登录页
        console.log('Unauthorized, redirecting to login...');
      }
    }
    
    return Promise.reject(error);
  }
);

并发请求

使用 axios.all 可以同时处理多个请求:

async function fetchMultipleData() {
  try {
    // 并发发送多个请求
    const [usersResponse, postsResponse] = await axios.all([
      apiClient.get('/users'),
      apiClient.get('/posts')
    ]);
    
    const users = usersResponse.data;
    const posts = postsResponse.data;
    
    return { users, posts };
  } catch (error) {
    handleApiError(error);
    throw error;
  }
}

如何封装?

1. 创建基础配置和实例

首先定义默认配置并创建 axios 实例,设置基础 URL、超时时间等。

// src/services/axios.ts
import axios, { AxiosInstance, AxiosRequestConfig} from 'axios';

// 基础配置
const baseConfig: AxiosRequestConfig = {
  baseURL: import.meta.env.VITE_API_BASE_URL || 'https://api.example.com',
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json',
  },
};

// 创建 axios 实例
const apiClient: AxiosInstance = axios.create(baseConfig);

export default apiClient;

2. 定义响应数据结构和错误类型

为了统一处理后端返回的数据格式,定义通用的响应接口和错误类型。

// src/types/api.ts
export interface ApiResponse<T = any> {
  code: number;
  message: string;
  data: T;
}

export class ApiError extends Error {
  constructor(
    public code: number,
    message: string,
    public data?: any
  ) {
    super(message);
    this.name = 'ApiError';
  }
}

3. 添加请求拦截器

在请求发送前添加认证信息(如 Token)、处理请求参数等。

// src/services/axios.ts
import { getToken } from './auth'; // 假设存在 auth 模块获取 token

// 请求拦截器
apiClient.interceptors.request.use(
  (config) => {
    // 获取 token 并添加到请求头
    const token = getToken();
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    
    // 处理请求参数(示例:添加时间戳防止缓存)
    if (config.method === 'get') {
      config.params = {
        ...config.params,
        _t: Date.now(),
      };
    }
    
    return config;
  },
  (error: AxiosError) => {
    console.error('Request error:', error.message);
    return Promise.reject(error);
  }
);

4. 添加响应拦截器

统一处理响应数据、错误码和异常情况。

// src/services/axios.ts
import { ApiResponse, ApiError } from '../types/api';

// 响应拦截器
apiClient.interceptors.response.use(
  (response: AxiosResponse<ApiResponse>) => {
    const { code, message, data } = response.data;
    
    // 根据业务状态码处理响应
    if (code === 200) {
      return data; // 直接返回 data,简化后续使用
    } else {
      // 业务错误(如 403、500 等)
      throw new ApiError(code, message, data);
    }
  },
  (error: AxiosError) => {
    // 网络错误或请求超时
    if (error.code === 'ECONNABORTED') {
      throw new ApiError(500, '请求超时,请稍后重试');
    }
    
    // HTTP 状态码错误
    if (error.response) {
      const { status, data } = error.response;
      
      switch (status) {
        case 401:
          // 未登录或 token 过期,跳转到登录页
          console.log('未授权,跳转到登录页');
          break;
        case 403:
          throw new ApiError(403, '权限不足', data);
        case 404:
          throw new ApiError(404, '资源不存在', data);
        case 500:
          throw new ApiError(500, '服务器内部错误', data);
        default:
          throw new ApiError(status, `请求错误 ${status}`, data);
      }
    }
    
    // 其他错误
    throw new ApiError(500, '网络连接失败,请检查网络设置', error.message);
  }
);

5. 封装通用请求方法

提供更简洁的请求 API,支持泛型以确保返回类型的准确性。

// src/services/request.ts
import apiClient from './axios';
import { ApiResponse } from '../types/api';

// 通用请求方法
export const request = {
  get: <T = any>(url: string, params?: any) => 
    apiClient.get<ApiResponse<T>>(url, { params }).then(res => res.data),
  
  post: <T = any>(url: string, data?: any) => 
    apiClient.post<ApiResponse<T>>(url, data).then(res => res.data),
  
  put: <T = any>(url: string, data?: any) => 
    apiClient.put<ApiResponse<T>>(url, data).then(res => res.data),
  
  delete: <T = any>(url: string, params?: any) => 
    apiClient.delete<ApiResponse<T>>(url, { params }).then(res => res.data),
};

6. 使用封装后的请求方法

在业务代码中使用封装好的请求方法,获得类型提示和错误处理。

// src/services/userService.ts
import { request } from './request';
import { User } from '../types/user';

// 获取用户列表
export const fetchUsers = () => 
  request.get<User[]>('/users');

// 创建用户
export const createUser = (user: Omit<User, 'id'>) => 
  request.post<User>('/users', user);
// 业务组件中使用
import { fetchUsers, createUser } from '../services/userService';

// 获取用户列表
fetchUsers()
  .then(users => console.log('用户列表:', users))
  .catch(error => console.error('获取用户失败:', error.message));

// 创建用户
createUser({ name: '张三', age: 25 })
  .then(newUser => console.log('创建成功:', newUser))
  .catch(error => console.error('创建失败:', error.message));

7. 环境配置

根据不同环境配置不同的 API 地址。

// .env.development
VITE_API_BASE_URL = 'https://dev-api.example.com'

// .env.production
VITE_API_BASE_URL = 'https://api.example.com'
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容