037 — @ohos/axios 网络请求封装:构建可复用的 HTTP 客户端

简介

在鸿蒙应用开发中,网络请求是连接客户端与服务端的核心桥梁。MoneyTrack 项目基于 @ohos/axios 库,通过单例模式封装了一个高度可复用的 HTTP 客户端,统一管理 baseURL、超时配置、请求/响应拦截器等关键能力。这套封装不仅减少了重复代码,更通过拦截器机制实现了全局错误处理、Token 注入、超时重试和日志打点,为上层 33+ 个 API 端点提供了稳定、一致的网络通信基础。

核心知识点

1. Axios 实例创建与全局配置

通过 axios.create() 创建自定义实例,集中管理全局配置,避免每个请求单独拼写:

  • baseURL:统一设置服务端地址,支持通过环境变量切换 dev/prod 环境。
  • 超时设置(30000ms):30 秒超时阈值,平衡用户体验与网络等待。配合超时重试机制,进一步提升弱网下的请求成功率。
  • 请求头默认值:统一设置 Content-Type: application/jsonAccept 等通用头部。
const instance = axios.create({
  baseURL: ApiConfig.getBaseUrl(), // 环境切换:dev/prod
  timeout: 30000,
  headers: { 'Content-Type': 'application/json' }
});

2. 拦截器机制

拦截器是 Axios 最强大的扩展点。MoneyTrack 使用多个拦截器实现关注点分离,多个请求拦截器按注册顺序先注册先执行,多个响应拦截器按注册顺序先注册后执行

请求拦截器链路:请求拦截器①(Token注入)→ 请求拦截器②(日志打点)→ 发送网络请求 → 响应拦截器①(状态码解析)→ 响应拦截器②(错误分类处理)

请求拦截器:注入 Token + 日志打点
// 请求拦截器① — Token 注入
instance.interceptors.request.use(
  (config) => {
    const token = AppStorage.get<string>('token');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => Promise.reject(error)
);

// 请求拦截器② — 日志打点
instance.interceptors.request.use(
  (config) => {
    console.info(`[Network] ${config.method?.toUpperCase()} ${config.url}`);
    return config;
  },
  (error) => Promise.reject(error)
);
响应拦截器:解析 data + 错误分类处理
// 响应拦截器① — 状态码校验
instance.interceptors.response.use(
  (response: AxiosResponse) => {
    if (response.status === 200) {
      return Promise.resolve(response.data);
    }
    Logger.error('[Network]', `请求失败:${response.config.url}`, JSON.stringify(response.status));
    return Promise.reject(response);
  },
  (error: BusinessError) => {
    // 错误分类处理
    if (error.code === 'ECONNABORTED') {
      // 超时错误
      return handleTimeoutRetry(error);
    } else if (!error.response) {
      // 断网错误
      ReminderUtil.showToast('网络异常,请检查网络连接');
    }
    Logger.error('[Network]', `请求失败:`, JSON.stringify(error));
    return Promise.reject(error);
  }
);

3. 超时重试机制

对于超时或可恢复的网络错误,采用"最大重试次数 + 固定延迟"的策略自动重试:

async function handleTimeoutRetry(error: BusinessError, retryCount = 3): Promise<any> {
  const config = error.config;
  if (!config || !config._retryCount) {
    config._retryCount = 0;
  }
  if (config._retryCount < retryCount) {
    config._retryCount++;
    console.info(`[Network] 第${config._retryCount}次重试:${config.url}`);
    return new Promise((resolve) => setTimeout(resolve, 1000))
      .then(() => instance.request(config));
  }
  return Promise.reject(error);
}

4. 环境切换

通过 ApiConfig 模块根据构建标识自动切换 baseURL,实现一套代码多环境部署:

export class ApiConfig {
  static getBaseUrl(): string {
    if (process.env.NODE_ENV === 'production') {
      return 'https://api.moneytrack.com';
    }
    return 'https://dev-api.moneytrack.com';
  }
}

请求拦截→发送→响应拦截完整链路

响应拦截器② 错误分类处理 响应拦截器① 状态码解析 后端服务 Axios 发送请求 请求拦截器② 日志打点 请求拦截器① Token 注入 应用层 API 响应拦截器② 错误分类处理 响应拦截器① 状态码解析 后端服务 Axios 发送请求 请求拦截器② 日志打点 请求拦截器① Token 注入 应用层 API alt [成功] [失败] 发起请求 读取 Token 并注入 Header 传递 config 打印请求日志 发送 HTTP 请求 网络传输 返回响应 接收响应 校验 status === 200 传递 response.data 返回业务数据 传递错误 分类处理(超时/断网/业务错误) Promise.reject

最佳实践

  1. Token 过期自动刷新:在响应拦截器中捕获 401 状态码,尝试调用刷新 Token 接口,成功后重放原始请求队列。
  2. 断网兜底:在网络不可用时,将请求暂存到离线队列,配合网络状态监听(见 041 篇)在网络恢复后自动重发。
  3. 拦截器顺序:多个请求拦截器按注册顺序依次执行;多个响应拦截器按注册顺序反向执行。需要先注入 Token 再打日志,因此 Token 拦截器先注册。
  4. 请求取消:使用 axios.CancelTokenAbortController,在页面离开时取消未完成的请求,避免内存泄漏。

推荐参考文档

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐