打造企业级axios二次封装,领导连忙点赞

4,240 阅读4分钟

1. 主文件 index.js

在主文件中,我们创建了一个 axios 实例,并设置了请求和响应拦截器。以下是如何在项目中进行配置和初始化 axios:

import axios from 'axios';
import qs from 'qs';
import addReqInterceptor from './reqInterceptor';
import addResInterceptor from './resInterceptor';


// 创建 axios 实例
const instance = axios.create({
  baseURL: process.env.$baseUrl, // 在环境变量中设置基础的请求 URL
  timeout: 1000 * 60,  // 设置请求超时时间,单位是毫秒
  withCredentials: true, // 跨域请求时是否需要携带 cookie
  paramsSerializer: function (params) {
    // 使用 qs 库对请求参数进行序列化,支持数组格式化
    return qs.stringify(params, { arrayFormat: 'indices' });
  },
});

// 添加请求拦截器
addReqInterceptor(instance);

// 添加响应拦截器
addResInterceptor(instance);

export default instance;

1.1 paramsSerializer

Axios 配置中,支持使用 params 对象作为参数。
当发起 GET 请求时,我可以设置 params 信息并且传递给 Axios,Axios 会根据 paramsSerializer 自动将其序列化并附加到 URL 中。

image.png

2. 请求拦截器 reqInterceptor.js

在请求拦截器中,主要的工作是:

  • 添加 Token:从存储中获取 Token 并将其放入请求头中。
  • 取消请求功能:如果请求需要取消,则为其添加取消令牌。
import { getToken } from "./auth"; // 假设有一个获取 token 的方法
import axios from "axios";

// 请求拦截器
export default function addReqInterceptor(axiosInstance) {
  axiosInstance.interceptors.request.use(
    (options) => {
      // 在 header 上携带 token
      addRequestToken(options);

      // 添加取消请求的配置项
      addCancelToken(options);

      return options;
    },
    (error) => {
      // 请求错误的处理
      console.warn(`Request interceptor error: ${error}`);
      return Promise.reject(error);
    }
  );
}

// 添加 Token 到请求头
function addRequestToken(options) {
  let tokenMsg = getToken(); // 获取 token
  if (tokenMsg) {
    tokenMsg = JSON.parse(tokenMsg);
    if (tokenMsg.token && tokenMsg.token !== "undefined") {
      // 将 token 放到请求头
      options.headers.token = tokenMsg.token;
    }
  }
}

// 取消请求
const source = {}; // 用于存储取消的请求
function addCancelToken(options) {
  const url = options.url; // 请求地址
  if (options.useCancel) {
    // 如果需要取消请求
    if (source[url]) {
      source[url].cancel(); // 取消之前的请求
    }
    // 获取一个CancelToken对象
    const CancelToken = axios.CancelToken;
    // source()将返回一个能取消请求的方法
    source[url] = CancelToken.source();
    options.cancelToken = source[url].token;
  }
}

2.1 携带 Token

很多 API 接口 只允许经过身份验证的用户访问。如果没有携带 TokenToken 无效,服务器会返回 401 错误,表示用户需要重新认证。在请求拦截器中默认添加,可减少每次添加Token的麻烦

2.2 取消请求场景

用户切换组件或表格:
    如果前一个请求尚未完成,而用户已经切换了组件或者表格,之前调用的相同的请求不再需要。如果仍然继续执行请求,则浪费了带宽和处理资源。

搜索输入时:

    在搜索功能中,用户输入时会触发自动搜索请求。如果用户连续输入或者输入过快,每个输入都会发出新的请求,前一个请求可以取消


3. 响应拦截器 resInterceptor.js

在响应拦截器中,主要工作是:

  • 检查 HTTP 状态码:如果状态码不在 2xx 范围内,请求出错需要抛出错误。
  • 检查返回的 code 字段:根据后端定义的返回代码处理不同的业务逻辑(如 token 过期、操作失败等)。
// 响应拦截器
export default function addResInterceptor(axiosInstance) {
  // 响应状态检查
  axiosInstance.interceptors.response.use(checkStatus, (error) => {
    return Promise.reject(error);
  });

  // 响应代码检查
  axiosInstance.interceptors.response.use(checkCodes, (error) => {
    return Promise.reject(error);
  });
}

// 响应状态检查
function checkStatus(response) {
  // 判断 HTTP 响应状态是否在 200~299 范围内
  if (response.status >= 200 && response.status < 300) {
    return response;
  }

  // 如果不在范围内,抛出错误
  const errorText = codeMessage[response.status] || response.statusText;
  const error = new Error(errorText);
  error.name = response.status;
  error.response = response;
  throw error;
}

// 响应代码检查
function checkCodes(response) {
  const data = response.data;

  // 根据不同的 code 执行不同的操作
  if (data.code === 10002 || data.code === 10004) {
    // 如果 code 为 10002 或 10004,表示登录失效,弹出重新登陆操作
    reLogIn(); 
} else if (data.code === -1) {
    // 如果 code 为 -1,表示操作失败,弹出提示
    // alert可以是内部项目封装的弹窗提示
    alert({
      text: data.message,
      icon: "info",
      button: "close",
    });
  }

  return response; // 返回原始响应数据
}

// 服务器返回的状态信息
const codeMessage = {
  200: "服务器成功返回请求的数据",
  201: "新建或修改数据成功",
  202: "一个请求已经进入后台排队(异步任务)中",
  204: "删除数据成功",
  400: "发出的请求有错误,服务器没有进行新建或修改数据的操作",
  401: "用户没有权限(令牌、用户名、密码错误)",
  403: "用户得到授权,但是访问是被禁止的",
  404: "发出的请求针对的是不存在的记录,服务器没有进行操作",
  406: "请求的格式不可得",
  410: "请求的资源被永久删除,且不会再得到",
  422: "当创建一个对象时,发生一个验证错误",
  500: "服务器发生错误,请检查服务器",
  502: "网关错误",
  503: "服务不可用,服务器暂时过载或维护",
  504: "网关超时",
};

在请求和响应拦截器中,我省略了部分项目业务独有的代码判断。您可以在对应的代码中加上你项目中独有的一些判断和修改传参,

4.封装get和post请求

    axios 返回的结果里不只是后端的结果信息,还有 configheaders 等信息。但是我们一般在开发中只需要其中的 data 信息。

axios 返回的结果如下

image.png

    为了减少每次请求都需要从 axios 返回的完整响应对象中提取 data 的操作,我们可以对 getpost 方法进行封装。

// 封装 GET 请求
export function get(url, options) {
  return instance
    .get(url, options)
    .then(response => response.data)  // 只返回 data 部分
    .catch(error => {
      console.error("请求 GET 错误:", error);
      return Promise.reject(error);  // 返回错误信息
    });
}

// 封装 POST 请求
export function post(url, options) {
  return instance
    .post(url, options)
    .then(response => response.data)  // 只返回 data 部分
    .catch(error => {
      console.error("请求 POST 错误:", error);
      return Promise.reject(error);  // 返回错误信息
    });
}

使用封装的 post 示例:

import { post } from './api';

const postData = {
  title: 'New Post',
  content: 'This is the content of the new post.',
};

post('/api/posts', postData)
  .then(data => {
    console.log('返回的数据:', data);
  })
  .catch(error => {
    console.error('请求失败:', error);
  });

总结:

  1. 请求拦截器:在请求发送之前,添加了 Token、默认头部、取消请求配置。
  2. 响应拦截器:检查 HTTP 状态码,并根据返回的 code 字段执行不同的业务操作,比如弹出提示或登出。
  3. 封装get和post请求: 请求成功后,直接返回 response.data,减少冗余代码。

提示: 如果您想了解 axios 的核心代码实现,可以参考:轻松掌握axios核心源码