axios代码规范与最佳实践

314 阅读4分钟

axios 是一个基于 Promise 的 HTTP 客户端,广泛用于前端与后台接口的交互。在 TypeScript 中,通过类型定义和接口约束,可以显著提高代码的可读性和安全性。本文将详细介绍如何使用 axios 进行规范的接口请求,并以 TypeScript 为例,重点讲解 GETPOST 请求的传参方式以及处理后台返回结果的实现。


1. 封装 axios 实例

通过封装 axios 实例,可以统一配置请求的基础 URL、超时时间、请求头等。

示例代码

import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';

// 创建 axios 实例
const instance: AxiosInstance = axios.create({
  baseURL: process.env.REACT_APP_API_BASE_URL || 'https://api.example.com', // 基础URL
  timeout: 10000, // 请求超时时间
  headers: {
    'Content-Type': 'application/json', // 默认请求头
  },
});

export default instance;

2. 统一处理请求和响应

使用拦截器统一处理请求和响应,例如添加认证 token、处理错误等。

请求拦截器

instance.interceptors.request.use(
  (config: AxiosRequestConfig) => {
    // 在请求发送之前添加认证 token
    const token = localStorage.getItem('token');
    if (token) {
      config.headers = config.headers || {};
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

响应拦截器

instance.interceptors.response.use(
  (response: AxiosResponse) => {
    // 对响应数据进行处理,假设服务器返回的数据结构是 { code, message, data }
    if (response.data.code === 200) {
      return response.data.data; // 只返回实际数据
    } else {
      // 如果 code 不是 200,抛出错误
      return Promise.reject(new Error(response.data.message || 'Request failed'));
    }
  },
  (error) => {
    // 对响应错误进行处理
    if (error.response) {
      switch (error.response.status) {
        case 401:
          // 处理未授权
          break;
        case 404:
          // 处理未找到
          break;
        default:
          // 处理其他错误
      }
    }
    return Promise.reject(error);
  }
);

3. GET 请求的传参与结果处理

GET 请求通常用于获取数据,参数通过 params 传递。

示例代码

interface User {
  id: number;
  name: string;
  age: number;
}

// 定义 GET 请求的返回类型
interface ApiResponse<T> {
  code: number;
  message: string;
  data: T;
}

// 获取用户信息
async function fetchUser(id: number): Promise<User> {
  try {
    const response = await instance.get<ApiResponse<User>>('/user', {
      params: { id }, // GET 请求的参数
    });
    return response.data.data; // 返回实际数据
  } catch (error) {
    console.error('Error fetching user:', error.message);
    throw error;
  }
}

// 调用示例
fetchUser(12345)
  .then((user) => {
    console.log('User data:', user);
  })
  .catch((error) => {
    console.error('Failed to fetch user:', error);
  });

4. POST 请求的传参与结果处理

POST 请求通常用于提交数据,参数通过 data 传递。

示例代码

interface CreateUserRequest {
  name: string;
  age: number;
}

// 创建用户
async function createUser(userData: CreateUserRequest): Promise<User> {
  try {
    const response = await instance.post<ApiResponse<User>>('/user', userData); // POST 请求的参数
    return response.data.data; // 返回实际数据
  } catch (error) {
    console.error('Error creating user:', error.message);
    throw error;
  }
}

// 调用示例
createUser({ name: 'John Doe', age: 30 })
  .then((user) => {
    console.log('User created:', user);
  })
  .catch((error) => {
    console.error('Failed to create user:', error);
  });

5. 文件上传

对于文件上传等特殊 POST 请求,可以使用 FormData 传递数据。

示例代码

async function uploadFile(file: File): Promise<string> {
  const formData = new FormData();
  formData.append('file', file);

  try {
    const response = await instance.post<ApiResponse<{ fileUrl: string }>>('/upload', formData, {
      headers: {
        'Content-Type': 'multipart/form-data',
      },
    });
    return response.data.data.fileUrl; // 返回文件地址
  } catch (error) {
    console.error('Error uploading file:', error.message);
    throw error;
  }
}

// 调用示例
const file = document.querySelector('input[type="file"]')?.files?.[0];
if (file) {
  uploadFile(file)
    .then((fileUrl) => {
      console.log('File uploaded:', fileUrl);
    })
    .catch((error) => {
      console.error('Failed to upload file:', error);
    });
}

6. 错误处理

catch 块中处理错误,或者使用响应拦截器统一处理错误。

示例代码

instance.get('/user')
  .then((data) => {
    console.log(data);
  })
  .catch((error) => {
    if (error.response) {
      // 请求已发出,但服务器响应状态码不在 2xx 范围内
      console.error('Server error:', error.response.data);
    } else if (error.request) {
      // 请求已发出,但没有收到响应
      console.error('No response received:', error.request);
    } else {
      // 其他错误
      console.error('Request setup error:', error.message);
    }
  });

7. 取消请求

在需要时取消请求,避免不必要的网络请求。

示例代码

import { CancelTokenSource } from 'axios';

let cancelTokenSource: CancelTokenSource;

async function fetchUserWithCancel(id: number): Promise<User> {
  cancelTokenSource = axios.CancelToken.source();

  try {
    const response = await instance.get<ApiResponse<User>>('/user', {
      params: { id },
      cancelToken: cancelTokenSource.token,
    });
    return response.data.data;
  } catch (error) {
    if (axios.isCancel(error)) {
      console.log('Request canceled:', error.message);
    } else {
      console.error('Error fetching user:', error.message);
    }
    throw error;
  }
}

// 取消请求
function cancelRequest() {
  if (cancelTokenSource) {
    cancelTokenSource.cancel('Operation canceled by the user.');
  }
}

8. 环境变量

将 API 的基础 URL 等配置放在环境变量中,便于在不同环境中切换。

示例代码

const instance: AxiosInstance = axios.create({
  baseURL: process.env.REACT_APP_API_BASE_URL,
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json',
  },
});

总结

在 TypeScript 中使用 axios 请求后台接口时,遵循以下规范和最佳实践:

  1. 封装 axios 实例:统一配置请求的基础 URL、超时时间、请求头等。
  2. 拦截器:统一处理请求和响应,例如添加认证 token、处理错误等。
  3. GET 请求:使用 params 传递参数,返回结果通过泛型定义类型。
  4. POST 请求:使用 data 传递参数,返回结果通过泛型定义类型。
  5. 文件上传:使用 FormData 传递文件数据。
  6. 错误处理:统一处理网络错误、服务器错误和业务逻辑错误。
  7. 取消请求:在需要时取消请求,避免不必要的网络请求。

通过以上规范,可以显著提高代码的可读性、可维护性和安全性。