HarmonyOS 鸿蒙应用开发 (七、HTTP网络组件 axios 介绍及封装使用)

在HarmonyOS应用开发中,通过HTTP访问网络,可以使用官方提供的@ohos.net.http模块。但是官方提供的直接使用不太好使用,需要封装下才好。推荐使用前端开发中流行的axios网络客户端库,如果是前端开发者,用 axios也会更加顺手。

目录

axios介绍

在HarmonyOS也能用Axios?

axios网络请求库的使用

下载安装

开通权限

简单使用

axios模块封装及使用

客户端封装

封装后使用

[官方@ohos/net.http 介绍](#官方@ohos/net.http 介绍)

官方简易封装

官方http模块封装使用

写在最后

其他资源

axios介绍

Axios 是一个著名的基于 JavaScript 的开源库,用于浏览器和 Node.js 等环境中发送 HTTP 请求。它支持 Promise API,并且可以处理 XMLHttpRequests 和 Fetch API 背后的复杂性,为开发者提供了一种简洁易用的方式来实现 AJAX(Asynchronous JavaScript and XML)请求。

最早浏览器页面在向服务器请求数据,返回的是整个页面的数据,页面会强制刷新一下,这对于用户来讲并不是很友好。我们只想修改页面的部分数据,但是从服务器端返回的却是整个页面。于是出现一种新的技术,异步网络请求Ajax(Asynchronous JavaScript and XML),它能与后台服务器进行少量数据交换,使网页实现异步局部更新。

由于浏览器中原生的XMLHttpRequest API较难使用,于是又有了更多用于实现ajax的javascript框架出现,比如我们熟悉的jQuery、Dojo、YUI等等。而如今一个叫axios的轻量框架逐步脱颖而出,它本质上也是对原生XHR的封装,只不过它是Promise的实现版本,符合最新的ES规范。

主要特性:

  1. 跨平台支持: Axios 可以在浏览器端通过 XMLHttpRequests 发送请求,在 Node.js 中则使用 http/https 模块发送请求。
  2. Promise API: Axios 的所有网络请求方法都返回 Promise 对象,使得异步编程更加简洁和易于处理。
  3. 拦截请求与响应: 提供了请求和响应的全局和实例级别的拦截器,可以在请求发送前或响应返回后进行预处理、错误处理或数据转换等操作。
  4. 取消请求: 支持主动取消已经发出但还未完成的HTTP请求。
  5. 自动转换JSON数据: Axios 自动将来自服务器的 JSON 数据转换为 JavaScript 对象,并且对于POST、PUT等请求体中的JSON数据也会自动序列化成字符串发送。
  6. 配置灵活性: Axios 允许自定义请求头、URL参数、超时时间等多种配置项,适用于不同场景下的API调用需求。
  7. 请求方法多样: 支持所有标准的HTTP方法(GET、POST、PUT、DELETE等),以及对PATCH等非标准方法的良好支持。
  8. 上传下载进度监控: Axios 还支持监听文件上传和下载的进度事件。

在HarmonyOS也能用Axios?

在HarmonyOS中,官方提供了@ohos/net.http 模块进行网络访问。它是官方提供的基础HTTP数据请求能力库,直接提供了对HTTP协议的底层支持,开发者可以通过这个模块发送GET、POST等HTTP请求,并处理响应结果。由于它是系统级别的API,其优点在于性能和兼容性得到保证,适用于基本的HTTP通信需求。

虽然官方提供了@ohos/net.http 模块进行网络访问,但是Axios库可以看作是一种功能更强大和易用的封装,且接口使用上更符合前端开发者的惯用习惯。Axios库 以其强大的功能性和易用性成为现代JavaScript应用中非常流行的HTTP客户端库。

直接使用原始的axios库肯定是不行,在HarmonyOS中的Axios库,模块名字是@ohos/axios。

@ohos/axios第三方库是基于axios库进行适配,使其可以运行在OpenHarmony中的一个发送网络请求库,并且本库沿用axios库现有用法和特性,使其更加适合于鸿蒙项目的开发。

@ohos/axios 模块可以理解为是对官方HTTP API的一个封装或者扩展,它提供了一种更高级别的抽象和便利性,可能包含了更多的功能特性,比如自动转换数据格式、错误处理、拦截器机制以及对于Promise的良好支持等,这些都是为了简化开发流程,提高开发效率。在实际开发应用时,如果需要更丰富和灵活的网络请求管理功能,通常推荐使用 @ohos/axios 这样的封装库。

通过对@ohos/axis源码的查看,发现也确实是使用ohos.net.http模块,对原库v1.3.4版本进行适配,使其可以应用在HarmonyOS上,并沿用其现有用法和特性。

  • http 请求
  • Promise API
  • request 和 response 拦截器
  • 转换 request 和 response 的 data 数据
  • 自动转换 JSON data 数据

ohos/axios 模块源码:

OpenHarmony-SIG/ohos_axios

axios网络请求库的使用

接口列表

接口 参数 功能
axios(config) config:请求配置 发送请求
axios.create(config) config:请求配置 创建实例
axios.request(config) config:请求配置 发送请求
axios.get(url[, config]) url:请求地址 config:请求配置 发送get请求
axios.delete(url[, config]) url:请求地址 config:请求配置 发送delete请求
axios.post(url[, data[, config]]) url:请求地址 data:发送请求体数据 config:请求配置 发送post请求
axios.put(url[, data[, config]]) url:请求地址 data:发送请求体数据 config:请求配置 发送put请求

属性列表

属性 描述
axios.defaults['xxx'] 默认设置 。值为请求配置 config 中的配置项 例如 axios.defaults.headers 获取头部信息
axios.interceptors 拦截器。参考 拦截器 的使用

下载安装

  • 方式一:在Terminal窗口中,执行如下命令安装三方包,DevEco Studio会自动在工程的oh-package.json5中自动添加三方包依赖。
bash 复制代码
ohpm install @ohos/axis

如果提示无ohpm命令,则是环境变量没有配置。 点击配置ohpm命令查看详细配置。

  • 方式二:在工程的oh-package.json5中设置三方包依赖,配置示例如下:
bash 复制代码
"dependencies": { "@ohos/axis": "^2.1.0"}

开通权限

需要配置 ohos.permission.INTERNET权限。在工程目录entry\src\main中找到module.json5文件,配置网络请求权限。

javascript 复制代码
{
  "module": {
    "name": "entry",
    "type": "entry",
    "description": "$string:module_desc",
    "mainElement": "EntryAbility",
    "deviceTypes": [
      "phone"
    ],
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      }
    ]
  }
}

简单使用

javascript 复制代码
import axios from '@ohos/axios'
//创建axios的实例
const instance = axios.create({
  baseURL: "http://xx.xx.xx.xx", //基路径,要看API帮助文档的特征来确定基路径
  timeout: 5000, //请求超时的时间
  headers: {
    "Content-Type": "application/json"
  }
})
 
//响应拦截器,通过响应拦截器进一步对返回的数据做处理
instance.interceptors.response.use((response) => {
  //只返回接口有数据的结果
  if (200 === response.status) {
    return response.data; //接口返回的数据
  }
  return Promise.reject(response); //表示请求有错,交给catch来处理结构
}, err => {
  return Promise.reject(err)
})
 
/**
 * get请求
 * @param params = {} 查询参数
 * @returns
 */
export function httpGet(url:string, params = {}) {
  return instance.get<any>(url, {
    params
  })
}
 
/**
 * post请求
 * @param data = {} 请求体数据
 * @returns
 */
export function httpPost(url:string, data = {}) {
  return instance.post<any>(url, {
   data 
  })
}

axios模块封装及使用

axios模块封装:

TypeScript 复制代码
//AxiosHttp.ets

import axios, {
  AxiosInstance,
  AxiosRequestConfig,
  AxiosRequestHeaders,
  AxiosResponse,
  InternalAxiosRequestConfig
} from "@ohos/axios";
import { LogUtils } from '../utils/LogUtils';


/**
 * 定义接口响应包装类
 */
export interface BaseResponse {
  //wanAndroid-API响应体
  errorCode: number
  errorMsg: string
  //拓展xxx-API响应体
}

/**
 * 接口实现类包装,例如有其他业务可以再次继承实现xxxResponse
 */
export interface ApiResponse<T = any> extends BaseResponse {
  //wanAndroid-API响应体
  data: T | any;
  //拓展xxx-API响应体
}

/**
 * 封装后,不支持传入拦截器
 * 需要自己定义接口继承 AxiosRequestConfig类型
 * 从而支持传入拦截器,但拦截器选项应为可选属性
 * 之后请求实例传入的options为继承了AxiosRequestConfig的自定义类型
 */
interface InterceptorHooks {
  requestInterceptor?: (config: HttpRequestConfig) => Promise<HttpRequestConfig>;
  requestInterceptorCatch?: (error: any) => any;
  responseInterceptor?: (response: AxiosResponse) => AxiosResponse | Promise<AxiosResponse>;
  responseInterceptorCatch?: (error: any) => any;
}

// @ts-ignore
interface HttpRequestConfig extends InternalAxiosRequestConfig {
  showLoading?: boolean; //是否展示请求loading
  checkResultCode?: boolean; //是否检验响应结果码
  checkLoginState?: boolean //校验用户登陆状态
  needJumpToLogin?: boolean //是否需要跳转到登陆页面
  interceptorHooks?: InterceptorHooks;
  headers?: AxiosRequestHeaders
}


/**
 * 网络请求构造
 * 基于axios框架实现
 */
class AxiosHttpRequest {
  config: HttpRequestConfig;
  interceptorHooks?: InterceptorHooks;
  instance: AxiosInstance;

  constructor(options: HttpRequestConfig) {
    this.config = options;
    this.interceptorHooks = options.interceptorHooks;
    this.instance = axios.create(options);
    this.setupInterceptor()
  }

  setupInterceptor(): void {
    this.instance.interceptors.request.use(
      //这里主要是高版本的axios中设置拦截器的时候里面的Config属性必须是InternalAxiosRequestConfig,但是InternalAxiosRequestConfig里面的headers是必传,所以在实现的子类我设置成非必传会报错,加了个忽略注解
      // @ts-ignore
      this.interceptorHooks?.requestInterceptor,
      this.interceptorHooks?.requestInterceptorCatch,
    );
    this.instance.interceptors.response.use(
      this.interceptorHooks?.responseInterceptor,
      this.interceptorHooks?.responseInterceptorCatch,
    );
  }

  // 类型参数的作用,T决定AxiosResponse实例中data的类型
  request<T = any>(config: HttpRequestConfig): Promise<T> {
    return new Promise<T>((resolve, reject) => {
      this.instance
        .request<any, T>(config)
        .then(res => {
          resolve(res);
        })
        .catch((err) => {
          LogUtils.error("网络请求Request异常:", err.message)
          errorHandler(err)
          if (err) {
            reject(err);
          }
        });
    });
  }

  get<T = any>(config: HttpRequestConfig): Promise<T> {
    return this.request({ ...config, method: 'GET' });
  }

  post<T = any>(config: HttpRequestConfig): Promise<T> {
    return this.request({ ...config, method: 'POST' });
  }

  delete<T = any>(config: HttpRequestConfig): Promise<T> {
    return this.request({ ...config, method: 'DELETE' });
  }

  patch<T = any>(config: HttpRequestConfig): Promise<T> {
    return this.request({ ...config, method: 'PATCH' });
  }
}

export function errorHandler(error: any) {
  if (error instanceof AxiosError) {
    showToast(error.message)
  } else if (error != undefined && error.response != undefined && error.response.status) {
    switch (error.response.status) {
    // 401: 未登录
    // 未登录则跳转登录页面,并携带当前页面的路径
    // 在登录成功后返回当前页面,这一步需要在登录页操作。
      case 401:

        break;
    // 403 token过期
    // 登录过期对用户进行提示
    // 清除本地token和清空vuex中token对象
    // 跳转登录页面
      case 403:
        showToast("登录过期,请重新登录")
      // 清除token
      // localStorage.removeItem('token');
        break;
    // 404请求不存在
      case 404:
        showToast("网络请求不存在")
        break;

    // 其他错误,直接抛出错误提示
      default:
        showToast(error.response.data.message)
    }

  }
}

export default AxiosHttpRequest

客户端封装

TypeScript 复制代码
//AxiosRequest.ets
import {AxiosHttpRequest,errorHandler} from './AxiosHttp'
import { AxiosError, AxiosRequestHeaders } from '@ohos/axios';
import { LogUtils } from '../utils/LogUtils';
import showToast from '../utils/ToastUtils';
import { hideLoadingDialog, showLoadingDialog } from '../utils/DialogUtils';
import { StorageUtils } from '../utils/StorageUtils';
import { StorageKeys } from '../constants/StorageKeys';
import { JsonUtils } from '../utils/JsonUtils';
import { Router } from '../route/Router';
import { RoutePath } from '../route/RoutePath';

/**
 * axios请求客户端创建
 */
const axiosClient = new AxiosHttpRequest({
  baseURL: "/api",
  timeout: 10 * 1000,
  checkResultCode: false,
  headers: {
    'Content-Type': 'application/json'
  } as AxiosRequestHeaders,
  interceptorHooks: {
    requestInterceptor: async (config) => {
      // 在发送请求之前做一些处理,例如打印请求信息
      LogUtils.debug('网络请求Request 请求方法:', `${config.method}`);
      LogUtils.debug('网络请求Request 请求链接:', `${config.url}`);
      LogUtils.debug('网络请求Request Params:', `\n${JsonUtils.stringify(config.params)}`);
      LogUtils.debug('网络请求Request Data:', `${JsonUtils.stringify(config.data)}`);
      axiosClient.config.showLoading = config.showLoading
      if (config.showLoading) {
        showLoadingDialog("加载中...")
      }
      if (config.checkLoginState) {
        let hasLogin = await StorageUtils.get(StorageKeys.USER_LOGIN, false)
        LogUtils.debug('网络请求Request 登录状态校验>>>', `${hasLogin.toString()}`);
        if (hasLogin) {
          return config
        } else {
          if (config.needJumpToLogin) {
            Router.push(RoutePath.TestPage)
          }
          throw new AxiosError("请登录")
        }
      }
      return config;
    },
    requestInterceptorCatch: (err) => {
      LogUtils.error("网络请求RequestError", err.toString())
      if (axiosClient.config.showLoading) {
        hideLoadingDialog()
      }
      return err;
    },
    responseInterceptor: (response) => {
      //优先执行自己的请求响应拦截器,在执行通用请求request的
      if (axiosClient.config.showLoading) {
        hideLoadingDialog()
      }
      LogUtils.debug('网络请求响应Response:', `\n${JsonUtils.stringify(response.data)}`);
      if (response.status === 200) {
        // @ts-ignore
        const checkResultCode = response.config.checkResultCode
        if (checkResultCode && response.data.errorCode != 0) {
          showToast(response.data.errorMsg)
          return Promise.reject(response)
        }
        return Promise.resolve(response.data);
      } else {
        return Promise.reject(response);
      }
    },
    responseInterceptorCatch: (error) => {
      if (axiosClient.config.showLoading) {
        hideLoadingDialog()
      }
      LogUtils.error("网络请求响应异常", error.toString())
      errorHandler(error);
      return Promise.reject(error);
    },
  }
});

export default axiosClient;

封装后使用

经过封装后,使用变得很简单了。示例如下:

TypeScript 复制代码
import axiosClient from './AxiosRequest'

let baseUrl = "https://www.wanandroid.com/"

//返回数据结构定义
interface HomeModelIssueList {
  releaseTime: number;
  type: string;
  date: number;
  publishTime: number;
  count: number;
}

interface HomeModel {
  issueList: HomeModelIssueList[];
  itemList: HomeModelIssueList[];
  nextPageUrl: string;
  nextPublishTime: number;
  newestIssueType: string;
}

interface BannerDataModelData {
  desc: string;
  id: number;
  imagePath: string;
  isVisible: number;
  order: number;
  title: string;
  type: number;
  url: string;
}

/**
 * 请求首页数据-axios客户端请求
 * @param date
 * @returns
 */
export function getHomeListAxios(date: string = "") {
  return axiosClient.get<HomeModel>({
    url: baseUrl + "api/v2/feed",
    params: { "date": date },
    showLoading: true
    // headers: { "Accept": "application/json" } as AxiosRequestHeaders
  })
}

/**
 * 获取分类详情接口
 * @param id
 * @param start
 */
export function getCategoryDetailList(id: number, start: number) {
  return axiosClient.get<HomeModel>(
    {
      url: baseUrl + "api/v4/categories/videoList",
      params: {
        "id": id,
        "start": start,
        "udid": CommonConstants.UUID,
        deviceModel: CommonConstants.DEVICE_NUM
      }
    }
  )
}

/**
 * 获取wanAndroid首页Banner数据,测试校验API data:T泛型数据
 * @returns
 */
export function getWanAndroidBanner() {
  return axiosClient.get<ApiResponse<BannerDataModelData[]>>(
    {
      url: wanAndroidUrl + "/banner/json",
      checkResultCode: true,
      showLoading: true
    }
  )
}

官方@ohos/net.http 介绍

在HarmonyOS(OpenHarmony)中,@ohos/net.http 是官方提供的一个用于进行HTTP通信的基础模块。开发者可以利用这个模块发送和接收HTTP请求与响应,实现应用程序与服务器之间的数据交互。

文档中心--HTTP数据请求

官方简易封装

@ohos/net.http模块直接使用起来不太友好,简易封装如下:

javascript 复制代码
//http.ets
/**
 * 定义接口响应包装类
 */
import http from '@ohos.net.http';

export interface BaseResponse {
  //wanAndroid-API响应体
  errorCode: number
  errorMsg: string
  //拓展xxx-API响应体
}

/**
 * 接口实现类包装,例如有其他业务可以再次继承实现xxxResponse
 */
export interface ApiResponse<T = any> extends BaseResponse {
  //wanAndroid-API响应体
  data: T | any;
  //拓展xxx-API响应体
}

interface HttpRequestConfig extends http.HttpRequestOptions {
  showLoading?: boolean; //是否展示请求loading
  checkResultCode?: boolean; //是否检验响应结果码
  checkLoginState?: boolean //校验用户登陆状态
  needJumpToLogin?: boolean //是否需要跳转到登陆页面
  url?: string, //请求网络链接
}


/**
 * 网络请求构造器
 * 基于鸿蒙默认的http框架实现
 */
class HttpBuilder {
  httpClient: http.HttpRequest
  config: HttpRequestConfig

  constructor(options: HttpRequestConfig) {
    this.httpClient = http.createHttp()
    this.config = options
    this.setupInterceptor()
  }
  /**
   * 配置属性拦截器
   */
  setupInterceptor() {

  }

  request<T = any>(config: HttpRequestConfig): Promise<T> {
    return new Promise<T>((resolve, reject) => {
      this.httpClient.request(
        config.url,
        config,
        (error, data) => {
          if (!error) {
            resolve(data.result as T);
          } else {
            reject(error)
          }
          // 当该请求使用完毕时,调用destroy方法主动销毁x
          this.httpClient.destroy()
        }
      )
    })
  }

  get<T = any>(config: HttpRequestConfig): Promise<T> {
    return this.request({ ...config, method: http.RequestMethod.GET })
  }

  post<T = any>(config: HttpRequestConfig): Promise<T> {
    return this.request({ ...config, method: http.RequestMethod.POST })
  }
}

export default HttpBuilder

官方http模块封装使用

TypeScript 复制代码
import http from '@ohos.net.http';
import showToast from '../utils/ToastUtils';
import HttpBuilder from './http';

//接口发送超时
const READ_TIMEOUT = 100000
//接口读取超时
const CONNECT_TIMEOUT = 100000

let baseUrl = "https://www.wanandroid.com/"

const httpClient = new HttpBuilder({
  readTimeout: READ_TIMEOUT,
  connectTimeout: CONNECT_TIMEOUT
})

//返回数据结构定义
interface HomeModelIssueList {
  releaseTime: number;
  type: string;
  date: number;
  publishTime: number;
  count: number;
}

interface HomeModel {
  issueList: HomeModelIssueList[];
  itemList: HomeModelIssueList[];
  nextPageUrl: string;
  nextPublishTime: number;
  newestIssueType: string;
}

/**
 * 请求数据--系统http请求
 * @param date
 * @returns
 */
export function getHomeList(date: string = "") {
  return httpClient.get<HomeModel>({
    url: baseUrl + "api/v2/feed",
    extraData: { "date": date }
  })
}

写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注博主,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新VIP学习资料,请关注猫哥公众号【猫青年】,回复"鸿蒙"获取

其他资源

太硬核!为什么一定要学习鸿蒙HarmonyOS系统开发?-CSDN博客

鸿蒙Harmony应用开发,一起来写一个"遥遥领先"的开眼App_开眼api-CSDN博客

鸿蒙HarmonyOS开发框架---学习ArkTS语言(状态管理 一)_鸿蒙os appstorage @storageprop @watch-CSDN博客

文档中心--HTTP数据请求

yang/ohos_axios

网络组件axios可以在OpenHarmony上使用了_@ohos/axios-CSDN博客

OpenHarmony-SIG/ohos_axios

TypeScript利用TS封装Axios实战_编程网

ts 封装 axios 技巧:充分利用类型检查与提示 - 掘金

相关推荐
钱塘江渔夫3 小时前
一键搭建OpenHarmony arkcompiler开发环境
openharmony·arkcompiler
lxysbly3 小时前
鸿蒙harmonyos端怀旧游戏模拟器,支持fc红白机 街机 gba psp ps1 nds n64世嘉md gbc gb sfc等主机
游戏·华为·harmonyos
ShuiShenHuoLe4 小时前
02Navigation页面路由
harmonyos·鸿蒙
飞凌嵌入式5 小时前
飞凌嵌入式RK3506J核心板通过OpenHarmony 5.1兼容性认证
嵌入式硬件·开源·鸿蒙
想你依然心痛5 小时前
HarmonyOS 5.0行业解决方案:基于端侧AI的智能工业质检APP开发实战
人工智能·华为·harmonyos
Sylus_sui5 小时前
鸿蒙 HarmonyOS 4.0+ 音乐播放器企业级完整实现(后台播放 + 系统播控中心 + 全功能)
华为·harmonyos
轻口味7 小时前
HarmonyOS 6 原生高性能相机框架:GPUImage (libgpuimagelib) 深度架构解析与实战全纪录
数码相机·架构·harmonyos
哦***78 小时前
华为Freebuds SE4 ANC降噪不稳定怎么回事?
华为·音频
小雨青年8 小时前
鸿蒙 HarmonyOS 6 | 网络请求超时重试与弱网适配深度解析
网络·华为·harmonyos
坚果的博客8 小时前
HarmonyOS Electron 适配器架构深度解析
华为·开源·harmonyos