news 2026/6/15 19:28:15

鸿蒙网络请求封装:基于 Axios 开发一个支持“自动刷新 Token”的 HTTP 客户端

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
鸿蒙网络请求封装:基于 Axios 开发一个支持“自动刷新 Token”的 HTTP 客户端

标签:#HarmonyOS #Axios #网络请求 #ArkTS #Token刷新 #架构设计


📉 前言:为什么原生 http 模块不够用?

原生http.createHttp()的痛点:

  1. 代码冗余:每次都要写extraDataconnectTimeout,还要手动解析 JSON。
  2. 缺乏拦截器:想给所有请求统一加 Header 或者统一处理错误,只能写个包装函数,很难维护。
  3. 类型弱:返回的数据全是string,需要手动JSON.parse并断言类型。

引入@ohos/axios后,我们不仅能复用前端的拦截器思维,还能利用 ArkTS 的泛型系统实现类型安全的网络层


🏗️ 一、 核心逻辑:Token 自动刷新流程

这是本篇的重难点。当多个并发请求同时触发 401 时,我们不能发起多次刷新请求,而是应该加锁。

并发控制流程图 (Mermaid):

401 未授权?

否 (我是第一个)

调用刷新接口

是 (别人正在刷)

刷新成功

解锁 & 广播

重发

刷新失败 (过期)

发起请求 A, B, C

响应拦截器

当前是否正在刷新?

加锁: isRefreshing = true

换取新 AccessToken

加入等待队列 (Promise Pending)

保存新 Token

执行队列中的请求

重发请求 A

强制登出

请求成功


🛠️ 二、 环境准备

安装鸿蒙版 Axios:

ohpminstall@ohos/axios

💻 三、 代码实战:企业级封装

我们新建一个AxiosRequest.ts文件。

1. 定义基础结构与类型

为了让调用者用得爽,我们先定义好返回结构。

importaxios,{AxiosInstance,AxiosRequestConfig,AxiosResponse,AxiosError}from'@ohos/axios';// 后端返回的标准结构interfaceBaseResponse<T>{code:number;message:string;data:T;}// 扩展 Axios 配置,允许传递自定义参数(如:是否需要 Loading)interfaceCustomRequestConfigextendsAxiosRequestConfig{showLoading?:boolean;}
2. 实现单例类与请求拦截

请求拦截器的作用很简单:有 Token 就带上

classAxiosHttpRequest{privateinstance:AxiosInstance;constructor(){this.instance=axios.create({baseURL:'https://api.example.com/v1',timeout:10000,headers:{'Content-Type':'application/json'}});// --- 请求拦截器 ---this.instance.interceptors.request.use((config:CustomRequestConfig)=>{// 从 AppStorage 或 Preferences 获取 Tokenconsttoken=AppStorage.Get<string>('accessToken');if(token){config.headers['Authorization']=`Bearer${token}`;}returnconfig;},(error)=>Promise.reject(error));// 响应拦截器在下一步实现...}}
3. 核心:响应拦截与无感刷新 (The Magic)

这里我们需要两个辅助变量:

  • isRefreshing: 防止多次调用刷新接口。
  • requestsQueue: 存储在刷新期间进来的其他请求。
// ... 类内部变量privateisRefreshing=false;privaterequestsQueue:Function[]=[];// ... 在 constructor 中继续添加响应拦截器this.instance.interceptors.response.use((response:AxiosResponse)=>{// 这里的逻辑根据你们后端业务码来定// 假设 http status 200 但 code 401 也是 token 过期constres=response.dataasBaseResponse<any>;if(res.code===401){returnthis.handle401Error(response.config);}returnresponse;},(error:AxiosError)=>{// 处理 HTTP 状态码为 401 的情况if(error.response?.status===401){returnthis.handle401Error(error.config);}returnPromise.reject(error);});// --- 处理 401 的核心逻辑 ---privatehandle401Error(originConfig:AxiosRequestConfig){if(!this.isRefreshing){this.isRefreshing=true;// 1. 发起刷新 Token 请求 (注意:这里最好用一个新的 axios 实例,避免死循环)returnthis.refreshToken().then((newToken)=>{// 2. 刷新成功,保存新 TokenAppStorage.SetOrCreate('accessToken',newToken);// 3. 修改原请求的 HeaderoriginConfig.headers['Authorization']=`Bearer${newToken}`;// 4. 执行队列中的请求this.requestsQueue.forEach(cb=>cb(newToken));this.requestsQueue=[];// 5. 重发当前请求returnthis.instance(originConfig);}).catch((err)=>{// 6. 刷新也失败了?那是真的过期了,去登录页吧this.requestsQueue=[];// router.pushUrl({ url: 'pages/Login' })returnPromise.reject(err);}).finally(()=>{this.isRefreshing=false;});}else{// 如果正在刷新,则把当前请求挂起,放入队列returnnewPromise((resolve)=>{this.requestsQueue.push((newToken:string)=>{originConfig.headers['Authorization']=`Bearer${newToken}`;resolve(this.instance(originConfig));});});}}// 模拟刷新 Token 的接口privateasyncrefreshToken():Promise<string>{// 实际业务中这里调用后端刷新接口// const refreshToken = AppStorage.Get('refreshToken');return"new_generated_token_123";}
4. 封装便捷方法 (GET/POST)

最后,暴露简单易用的 API。

// T 是返回数据的类型,D 是请求参数的类型publicget<T>(url:string,params?:any):Promise<T>{returnthis.instance.get<BaseResponse<T>>(url,{params}).then(res=>res.data.data);}publicpost<T>(url:string,data?:any):Promise<T>{returnthis.instance.post<BaseResponse<T>>(url,data).then(res=>res.data.data);}}// 导出单例exportconsthttp=newAxiosHttpRequest();

🚀 四、 调用演示:丝滑体验

在你的 UI 组件 (.ets) 中:

import{http}from'../utils/AxiosRequest';interfaceUserProfile{id:number;name:string;}@Entry@Componentstruct ProfilePage{@Stateuser:UserProfile|null=null;asyncaboutToAppear(){try{// 泛型支持:res 自动推断为 UserProfile 类型// 哪怕 Token 过期,这里也会自动重试并成功返回constres=awaithttp.get<UserProfile>('/user/profile');this.user=res;console.info('用户名称:',res.name);}catch(error){console.error('请求失败:',error);}}build(){// UI ...}}

🎯 总结

通过这次封装,我们实现了:

  1. 代码解耦:UI 层不需要关心 Token 怎么传,也不需要关心 401 怎么处理。
  2. 类型安全:利用 TypeScript 泛型,接口返回什么类型,代码里就是什么类型。
  3. 极致体验Request Queue (请求队列)的设计,确保了在并发请求场景下,Token 刷新接口只会被调用一次,避免了资源浪费和逻辑错误。

这是鸿蒙 App 开发中性价比最高的基建工作之一。

Next Step:
现在的封装还没处理Loading 动画。试着修改Interceptor,在请求开始时调用promptAction.showToast或自定义 Loading 组件,请求结束时关闭它,实现全局自动 Loading。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/15 12:19:31

Python注释入门:写给零基础小白的完整指南

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个交互式Python注释学习应用&#xff1a;1. 提供注释基础知识讲解 2. 展示好的和坏的注释示例 3. 提供实时练习环境 4. 自动检查用户编写的注释 5. 给出改进反馈。要求界面友…

作者头像 李华
网站建设 2026/6/15 11:19:34

ResNet18缺陷检测案例:3步复现工业级应用

ResNet18缺陷检测案例&#xff1a;3步复现工业级应用 引言 作为一名工厂技术员&#xff0c;你可能经常在论文里看到各种先进的AI质检方案&#xff0c;但回到现实却发现厂里连像样的AI服务器都没有。这种"纸上谈兵"的落差感我太理解了——直到我发现用ResNet18这样轻…

作者头像 李华
网站建设 2026/6/15 13:39:47

StructBERT零样本分类优化技巧:提升分类精度的5个方法

StructBERT零样本分类优化技巧&#xff1a;提升分类精度的5个方法 1. 引言&#xff1a;AI 万能分类器的时代来临 在当今信息爆炸的时代&#xff0c;文本数据的自动化处理已成为企业智能化转型的核心需求。无论是客服工单、用户反馈、新闻资讯还是社交媒体内容&#xff0c;都需…

作者头像 李华
网站建设 2026/6/15 12:25:02

Rembg抠图API高级用法:自定义参数详解

Rembg抠图API高级用法&#xff1a;自定义参数详解 1. 智能万能抠图 - Rembg 在图像处理与内容创作日益普及的今天&#xff0c;自动去背景技术已成为设计师、电商运营、AI开发者不可或缺的工具。传统手动抠图耗时耗力&#xff0c;而基于深度学习的智能抠图方案正逐步成为主流。…

作者头像 李华
网站建设 2026/6/15 12:13:11

Rembg抠图速度测试:不同分辨率图片对比

Rembg抠图速度测试&#xff1a;不同分辨率图片对比 1. 智能万能抠图 - Rembg 在图像处理与内容创作领域&#xff0c;自动去背景&#xff08;Image Matting / Background Removal&#xff09;是一项高频且关键的需求。无论是电商商品图精修、社交媒体内容制作&#xff0c;还是…

作者头像 李华