🧱 优雅封装 Axios 请求:从错误处理到统一响应

393 阅读4分钟

在前端开发中,我们经常需要与后端 API 进行交互。为了提高代码的可维护性、减少重复逻辑,并提升开发效率,对 axios 的请求进行合理的封装是非常有必要的。

本文将带你一步步了解如何通过一个通用的 to 函数,优雅地封装 axios 请求,实现:

  • 统一处理业务异常;
  • 简化异步调用流程;
  • 避免层层嵌套的 if (res.code !== 0) 判断;
  • 提高代码可读性和复用性。

🧩 背景问题:传统写法的痛点

假设你有一个接口调用如下:

js
深色版本
let res = await getMerchantOrder(data);
if (res.code != 0) {
  showToast(res.msg);
  return false;
}

这种写法虽然功能正常,但存在几个明显的问题:

  1. 每个请求都需要手动判断 res.code
  2. 错误提示分散,不易统一管理
  3. 返回值结构不一致,不利于后续处理
  4. 难以集中处理网络层和业务层的错误

这会导致你的业务代码中充斥大量“防御性判断”,严重影响可读性。


✨ 解决方案:使用 to 函数统一处理请求结果

我们可以创建一个名为 to 的辅助函数,用于包装任何基于 Promise 的请求(如 axios),并返回一个标准格式的 [error, data] 结构。

🔨 实现代码如下:

ts
深色版本
/**
 * 将 Promise 包装成 [error, data] 形式,简化异步操作错误处理
 * @param {Promise} promise - 需要包装的 Promise 对象
 * @param {*} errorExt - 可选,附加的错误信息对象
 * @returns {Array} [error, data]
 */
export const to = async (promise, errorExt) => {
  try {
    const res = await promise;

    // 如果业务状态码不是成功状态(例如 code !== 0)
    if (res?.code !== 0) {
      const errorMessage = res?.msg ?? '获取数据失败';
      showToast(errorMessage);
      return [new Error(errorMessage), null];
    }

    // 成功时返回 null 错误 + 数据
    return [null, res.data];

  } catch (err) {
    // 捕获异常并返回 [error, null]
    let parsedError = err;

    if (errorExt) {
      parsedError = Object.assign({}, err, errorExt);
    }

    return [parsedError, null];
  }
};

💡 注意:这里的 showToast 是你项目中已有的 UI 提示方法,比如 uni.showToastElMessage 或自定义 Toast 工具。


📦 使用方式:简洁又直观

现在你可以这样使用:

ts
深色版本
const [err, data] = await to(getMerchantOrder(data));

if (err) {
  console.error('请求失败:', err.message);
  return;
}

// 正常处理 data
console.log('订单数据:', data);

✅ 优势总结:

特性描述
✅ 统一错误处理所有错误都在 err 中返回
✅ 清晰结构返回 [err, data],无需 try/catch 嵌套
✅ 减少冗余不再需要 if (res.code !== 0) 处理
✅ 可扩展性强支持自定义错误信息注入

🧪 示例场景对比

❌ 原始写法:

js
深色版本
async function fetchOrder() {
  try {
    const res = await getMerchantOrder(data);
    if (res.code !== 0) {
      showToast(res.msg);
      return;
    }
    // do something with res.data
  } catch (err) {
    showToast('网络异常');
  }
}

✅ 使用 to 后:

js
深色版本
async function fetchOrder() {
  const [err, data] = await to(getMerchantOrder(data));

  if (err) {
    // 统一错误提示 + 日志记录
    console.error('请求出错', err.message);
    return;
  }

  // 直接处理 data
  console.log('订单数据:', data);
}

是不是更加清爽了?


🧰 高级用法:结合 TypeScript 更加安全

如果你使用的是 TypeScript,可以为 to 添加类型支持:

ts
深色版本
type ResponseData<T> = {
  code: number;
  msg?: string;
  data?: T;
};

export async function to<T>(promise: Promise<ResponseData<T>>, errorExt?: any): Promise<[Error | null, T | null]> {
  try {
    const res = await promise;

    if (res.code !== 0) {
      const msg = res.msg || '获取数据失败';
      showToast(msg);
      return [new Error(msg), null];
    }

    return [null, res.data as T];
  } catch (err) {
    const error = errorExt ? Object.assign({}, err, errorExt) : err;
    return [error, null];
  }
}

这样在 IDE 中就可以获得完整的类型提示和自动补全支持!


🧠 总结:让请求更优雅、更可控

通过对 Axios 请求的封装,我们实现了:

  • 统一的错误处理机制
  • 标准化的数据结构返回
  • 更清晰的业务代码逻辑
  • 更强的可维护性和可测试性

无论你是开发小程序、Web 应用,还是构建中后台系统,这样的封装都能极大提升开发体验和代码质量。


📚 扩展建议

如果你希望进一步增强这个封装工具,还可以考虑加入以下功能:

  • 自动重试机制(如网络失败时 retry);
  • 请求拦截器/响应拦截器;
  • 全局错误上报;
  • 接口 Mock 支持;
  • 请求缓存策略;
  • 支持取消请求(AbortController);

📌 最后提醒:
不要把所有请求都写得“千篇一律”,封装是手段,统一和规范才是目的


如需我帮你生成一个完整的 Axios 封装模块(包含拦截器、TypeScript 支持、Mock 等),欢迎继续提问 👍

你可以将这篇文章发布到:

  • 掘金 / CSDN / 博客园 / 知乎 / 微信公众号 / Notion / 内部 Wiki
  • 或者作为团队编码规范的一部分共享给同事