news 2026/10/31 7:49:06

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

作者头像

张小明

前端开发工程师

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

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


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

原生http.createHttp()的痛点:

  1. 代码冗余:每次都要写extraData、connectTimeout,还要手动解析 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/10/30 2:24:26

没GPU如何学ResNet18?云端1小时1块,比网吧便宜

没GPU如何学ResNet18&#xff1f;云端1小时1块&#xff0c;比网吧便宜 引言 作为一名高中生参加AI夏令营&#xff0c;你可能正面临一个现实问题&#xff1a;家里电脑是集成显卡&#xff0c;学校机房又不开放&#xff0c;但老师布置了ResNet18模型实验作业。别担心&#xff0c…

作者头像 李华
网站建设 2026/10/31 5:09:00

5分钟快速搭建iSCSI测试环境

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个基于Docker的一键式iSCSI测试环境部署脚本。要求&#xff1a;1) 自动部署iSCSI target和initiator容器 2) 支持多种Linux发行版基础镜像 3) 包含示例LUN创建和挂载脚本 4)…

作者头像 李华
网站建设 2026/10/29 11:55:18

零基础Linux下NGINX安装图文指南

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个面向Linux新手的NGINX安装指南&#xff0c;要求&#xff1a;1. 使用Ubuntu 22.04桌面版演示 2. 每个步骤配截图 3. 包含终端命令复制按钮 4. 常见错误解决方案 5. 验证安装…

作者头像 李华
网站建设 2026/10/28 13:31:41

ResNet18模型转换指南:云端一站式解决ONNX/TFLite

ResNet18模型转换指南&#xff1a;云端一站式解决ONNX/TFLite 引言 作为一名移动端AI开发者&#xff0c;你是否经常遇到这样的困扰&#xff1a;好不容易训练好的ResNet18模型&#xff0c;在部署到移动设备时却频频报错&#xff1f;本地环境配置复杂&#xff0c;各种依赖冲突导…

作者头像 李华
网站建设 2026/10/29 22:06:46

1小时搭建百万QPS原型:快速验证你的高并发想法

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个快速原型项目模板&#xff0c;支持&#xff1a;1. 一键部署到云平台 2. 自动生成负载测试脚本 3. 预配置高性能组件(Redis,CDN等) 4. 可视化QPS监控 5. 成本估算功能。使用…

作者头像 李华
网站建设 2026/10/29 16:03:56

OAuth2小白入门:5分钟看懂授权流程

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个OAuth2.0教学演示项目&#xff0c;要求&#xff1a;1.用最简代码展示四种授权模式差异 2.每一步都有可视化流程图 3.包含交互式演示界面 4.提供逐步讲解文档 5.支持本地运…

作者头像 李华