鸿蒙Next - 基于Axios对网络请求进行二次封装

前言

hello 大家好!我是无言。 最近鸿蒙相关的开发需求日渐增多,我基于 @ohos.net.http进行过封装用于项目开发,也有用axios开发过项目,其实整个的逻辑是非常相似的,但是作为一个前端开发人员来讲,axios使用起来还是更加符合我们平时的开发习惯。所以本文来讲一讲如何封装axios用于实际的鸿蒙项目开发。

一、准备工作

  • 安装好最新DevEco Studio 开发工具,创建一个新的空项目。

  • 下载安装 @ohos/axios

在项目对应模块下oh-package.json5文件 dependencies中输入"@ohos/axios": "2.2.2"

完成后参考下图点击执行 Sync Now 进行安装

  • 创建网络请求工具文件 ets/utils/request.ets
  • 创建统一管理接口文件 ets/api/index.ets

二、利用axios的create方法创建 axios 实例

这里的作用主要有下面三个:

  • 配置我们全局公共的网络请求前缀 baseURL
  • 配置统一的超时时间 timeout
  • 配置公共的统一 headers
request.ets 复制代码
import axios,{InternalAxiosRequestConfig, AxiosResponse,AxiosError,AxiosRequestConfig,AxiosInstance} from '@ohos/axios';
// 创建实例
const instance: AxiosInstance = axios.create({
  baseURL: 'http://XXX.XX.XX.XX', //修改为自己项目的实际地址
  timeout: 10000,//超时
  headers: { 'Content-Type': 'application/json' },
 
// `transformRequest` 允许在向服务器发送前,修改请求数据 一般来说用不到,只要后端同志是正常的
// 它只能用于 'PUT', 'POST' 和 'PATCH' 这几个请求方法
// 数组中最后一个函数必须返回一个字符串, 一个Buffer实例,ArrayBuffer,FormData,或 Stream
// 你可以修改请求头。
// transformRequest: [(data: ESObject, headers: AxiosRequestHeaders) => {
//   // 对发送的 data 进行任意转换处理
//   // console.log('api1Result出零四',data)
//   // return data
// }],

});

三、配置添加请求拦截器

这里运用最多的场景也就是把身份信息Token放入到header中携带过去传到后端。 这里还涉及到缓存信息,这个每个项目都有自己的考量,我在项目中一般用的腾讯的@tencent/mmkv,体验感还不错。

javascript 复制代码
// 请求拦截器
instance.interceptors.request.use((config:InternalAxiosRequestConfig) => {
  // 对请求数据做点什么
  const  token = 'eyJhbw'//自己根据自己项目实际情况获取
  if(token){
    config.headers['token']=token //设置token
  }
  return config;
}, (error:AxiosError) => {
  // 对请求错误做些什么 
  return Promise.reject(error);
});

四、配置添加响应拦截器

下面内容用到了@ohos.router进行页面 跳转,用到了@kit.ArkUI'promptAction.showToast 进行统一错误提示。

响应拦截非常重要,他是我们封装请求好用的关键,但是每个项目业务不同,其实响应拦截的配置都有不小的差异,但是整体思路是一致的,所以要根据自己的实际情况进行修改。

javascript 复制代码
// 添加响应拦截器
instance.interceptors.response.use((response:AxiosResponse)=> {
  // 对响应数据做点什么
  // 因为 我们后台返回的 数据结构是统一的例如{code:2001,massage:'用户信息不能为空',data:{}}
  // 所以下面配置根据系统返回来配置的,不同的系统配置不同
  if(response.data.code=='2001'){ //这里是举例 要根据自己项目的实际情况进行处理
    promptAction.showToast({ //用到了 @kit.ArkUI 的 promptAction进行系统弹窗提示
      message: response.data.massage,
      duration: 2000,
      alignment:Alignment.Center
    });
    return Promise.reject(response.data);
  }
  if(response.data.code=='4001'){//这里是举例 要根据自己项目的实际情况进行处理
    router.pushUrl({ //用到了 @ohos.router 进行页面跳转
      url:"pages/LoginPage"
    })
    return Promise.reject(response.data);
  }
  return response.data;
}, (error:AxiosError)=> {
  console.log("AxiosError",JSON.stringify(error.response))
  const status = error.response?.status;
  switch (status) {
    case 401://无权限未登录
      router.pushUrl({ //用到了 @ohos.router 进行页面跳转
        url:"pages/LoginPage"
      })
      break;
    default :
      promptAction.showToast({ //用到了 @kit.ArkUI 的 promptAction进行系统弹窗提示
        message: "系统异常,请稍后再试!",
        duration: 2000,
        alignment:Alignment.Center
      });
      break
  }
  return Promise.reject(error);
});

五、使用泛型T暴露我们请求的配置

在本例中我针对后端返回的数据结构如下

json 复制代码
{
    "code": 2001, 
    "message": "接口调用失败",
    "data": { } //data的内容不是固定的可能是null,可能是对象,也可能是数组
}

因为我们接口返回结果不固定,所以我们利用一下Ts的 泛型暴露我们封装的请求。

axios封装的完整代码如下

typescript 复制代码
import axios,{InternalAxiosRequestConfig, AxiosResponse,AxiosError,AxiosRequestConfig,AxiosInstance} from '@ohos/axios';
import router from '@ohos.router';
import { promptAction } from '@kit.ArkUI';


interface ApiResponse<T> { //根据项目实际项目修改
  data: T;
  message?: string;
  code?: number;
}
// 创建实例
const instance: AxiosInstance = axios.create({
  baseURL: 'http://XXX.XXX.XXX', //修改为自己项目的实际地址
  timeout: 10000,//超时
  headers: { 'Content-Type': 'application/json' },
});

//暴露封装请求--为什么没有把上面实例放入到下面中,避免每一个接口都去创建一次
//这里利用了泛型 T 
export default <T>(config:AxiosRequestConfig): Promise<ApiResponse<T>>=> {

// 请求拦截器
instance.interceptors.request.use((config:InternalAxiosRequestConfig) => {
  // 对请求数据做点什么
  const  token = 'eyJhbGciOiJ....'//自己根据自己项目实际情况获取我大部分用的 @tencent/mmkv 插件
  if(token){
    config.headers['authorization']=token //设置token
  }
  return config;
}, (error:AxiosError) => {
  // 对请求错误做些什么
  return Promise.reject(error);
});

// 添加响应拦截器
instance.interceptors.response.use((response:AxiosResponse)=> {
  // 对响应数据做点什么
  // 因为 我们后台返回的 数据结构是统一的例如{code:2001,massage:'用户信息不能为空',data:{}}
  // 所以下面配置根据系统返回来配置的,不同的系统配置不同
  if(response.data.code=='2001'){ //这里是举例 要根据自己项目的实际情况进行处理
    promptAction.showToast({ //用到了 @kit.ArkUI 的 promptAction进行系统弹窗提示
      message: response.data.massage,
      duration: 2000,
      alignment:Alignment.Center
    });
    return Promise.reject(response.data);
  }
  if(response.data.code=='4001'){//这里是举例 要根据自己项目的实际情况进行处理
    router.pushUrl({ //用到了 @ohos.router 进行页面跳转
      url:"pages/LoginPage"
    })
    return Promise.reject(response.data);
  }
  return response.data;
}, (error:AxiosError)=> {
  console.log("AxiosError",JSON.stringify(error.response))
  const status = error.response?.status;
  switch (status) {
    case 401://无权限未登录
      router.pushUrl({ //用到了 @ohos.router 进行页面跳转
        url:"pages/LoginPage"
      })
      break;
    default :
      promptAction.showToast({ //用到了 @kit.ArkUI 的 promptAction进行系统弹窗提示
        message: "系统异常,请稍后再试!",
        duration: 2000,
        alignment:Alignment.Center
      });
      break
  }
  return Promise.reject(error);
});

return instance(config);
}

六、代码示例示范统一管理接口配置(测试)

ets/api/index.ets文件添加如下内容

csharp 复制代码
import request from "../utils/request";

interface LoginParams{
  phone:string;
  code:string|number;
}


//测试登录
export function TestLogin<T>(data:LoginParams){
  return request<T>({
    url: "/test/login",
    method: "post",
    data
  });
}

//测试信息列表
export function parkList<T>(params:Record<string,string>){
  return request<T>({
    url: "/test/parks",
    method: "get",
    params
  });
}

七、代码示例示范调用(测试)

typescript 复制代码
import {TestLogin } from "../api/index"
// 定义返回内容
interface ResponseLogin{
  name:string;
  code:string
}
@Entry
@Component
struct Index {

  async setTestLogin(){
    const result = await TestLogin<ResponseLogin>({phone:'13111111',code:352})
    if(result){
      console.log('result',JSON.stringify(result))
      console.log('code',JSON.stringify(result.code))
      console.log('data',JSON.stringify(result.data))
    }
  }

  build() {
    RelativeContainer() {
      Button('测试').onClick(()=>{
        this.setTestLogin();
      })
    }
    .height('100%')
    .width('100%')
  }
}

八、其他题外话

我们知道在鸿蒙开发中类型校验非常严格,而且不允许有 any 类型,如果项目赶期,声明类型是一个非常麻烦的事情。其实鸿蒙开发中也有办法,就是不要用 .ets后缀结尾。 可以 声明一个type.d.ts 文件,然后再其他.ets 文件中引入是不会报错的。

所以上面封装axios文件中 你要是嫌麻烦,也可以用下面的 类型去代替泛型T。(不建议)

type.d.ts 复制代码
//嵌套未知类型
export  interface PromiseObject {
  [key: string]: string | number |object| boolean |undefined| PromiseObject | PromiseObject[] | Array<string | number | boolean | PromiseObject |any>
}

// any 类型
export  type anyType = any;

总结

上面针对Axios的封装,基本上就能够满足大部分业务需求了,当然还有取消请求,断开重连这些我认为用到的比较少,就没有加上,如果后续掘友们觉得有必要 我可以再加上。

本篇文章写到这里就结束了,我把代码放到了gitee上,如果觉得本篇文章对你有帮助欢迎来波三连。

相关推荐
燃先生._.18 分钟前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖1 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235241 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_748240252 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar2 小时前
纯前端实现更新检测
开发语言·前端·javascript
寻找沙漠的人3 小时前
前端知识补充—CSS
前端·css
AORO_BEIDOU3 小时前
单北斗+鸿蒙系统+国产芯片,遨游防爆手机自主可控“三保险”
华为·智能手机·harmonyos
GISer_Jing3 小时前
2025前端面试热门题目——计算机网络篇
前端·计算机网络·面试
m0_748245523 小时前
吉利前端、AI面试
前端·面试·职场和发展