鸿蒙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上,如果觉得本篇文章对你有帮助欢迎来波三连。

相关推荐
蓝枫amy2 小时前
HarmonyOS快速入门
华为·harmonyos
桂月二二4 小时前
探索前端开发中的 Web Vitals —— 提升用户体验的关键技术
前端·ux
hunter2062065 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
qzhqbb6 小时前
web服务器 网站部署的架构
服务器·前端·架构
刻刻帝的海角6 小时前
CSS 颜色
前端·css
九酒6 小时前
从UI稿到代码优化,看Trae AI 编辑器如何帮助开发者提效
前端·trae
浪浪山小白兔7 小时前
HTML5 新表单属性详解
前端·html·html5
程序猿阿伟7 小时前
《探秘鸿蒙Next:如何保障AI模型轻量化后多设备协同功能一致》
人工智能·华为·harmonyos
lee5767 小时前
npm run dev 时直接打开Chrome浏览器
前端·chrome·npm
2401_897579657 小时前
AI赋能Flutter开发:ScriptEcho助你高效构建跨端应用
前端·人工智能·flutter