news 2026/6/10 2:51:42

鸿蒙网络请求封装:基于 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/9 19:47:42

零样本分类性能优化:AI万能分类器加速技巧

零样本分类性能优化&#xff1a;AI万能分类器加速技巧 1. 引言&#xff1a;为什么需要零样本分类的性能优化&#xff1f; 随着企业对非结构化文本数据处理需求的激增&#xff0c;自动文本分类已成为智能客服、工单系统、舆情监控等场景的核心能力。传统分类模型依赖大量标注数…

作者头像 李华
网站建设 2026/6/9 23:36:45

电商支付系统RSA公钥缺失实战解决方案

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个电商支付系统密钥管理演示项目&#xff0c;模拟以下场景&#xff1a;1. 支付时出现RSA PUBLIC KEY NOT FIND错误 2. 展示完整的排查流程 3. 实现三种解决方案&#xff1a;…

作者头像 李华
网站建设 2026/6/6 6:44:51

1小时搭建数字普惠金融指数可视化看板

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 快速开发一个数字普惠金融指数可视化看板&#xff0c;要求&#xff1a;1. 接入模拟数据API&#xff1b;2. 实现省级地图着色可视化&#xff1b;3. 时间轴动态播放功能&#xff1b;…

作者头像 李华
网站建设 2026/6/9 19:58:29

计算机毕设java学生宿舍管理系统 基于Java的高校学生宿舍智能化管理系统设计与实现 Java技术驱动的学生宿舍综合管理平台开发

计算机毕设java学生宿舍管理系统0044z9&#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。随着信息技术的飞速发展&#xff0c;高校学生宿舍管理逐渐从传统的人工模式向智能化、信息…

作者头像 李华
网站建设 2026/6/9 22:08:12

效率革命:AI自动化测试VS手工测试数据对比

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 构建一个测试效率对比分析工具&#xff0c;能够&#xff1a;1)接收手工测试用例和AI生成测试用例&#xff1b;2)自动统计执行时间、缺陷发现数量和测试覆盖率&#xff1b;3)生成可…

作者头像 李华