HarmonyOS 鸿蒙Next在应用开发中使用 Axios 封装网络请求

发布于 1周前 作者 songsunli 来自 鸿蒙OS

HarmonyOS 鸿蒙Next在应用开发中使用 Axios 封装网络请求
<markdown _ngcontent-hog-c147="" class="markdownPreContainer">

在 HarmonyOS 应用开发中,通过 HTTP 访问网络,可以使用官方提供的 [@ohos](/user/ohos).net.http 模块。但是官方提供的直接使用不太友好,需要封装才能更方便地使用。推荐使用前端开发中流行的 Axios 网络客户端库。对于前端开发者来说,使用 Axios 会更加顺手。

目录

  • Axios 介绍
  • 在 HarmonyOS 中使用 Axios
  • axios 网络请求库的使用
    • 下载安装
    • 开通权限
    • 简单使用
  • axios 模块封装及使用
    • 客户端封装
    • 封装后使用
  • 官方 [@ohos](/user/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 规范。

主要特性:

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

在 HarmonyOS 中使用 Axios

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

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

直接使用原始的 Axios 库肯定是不行的,因此在 HarmonyOS 中,我们使用了一个名为 [@ohos](/user/ohos)/axios 的第三方库,它是基于 Axios 库进行适配,使其可以运行在 OpenHarmony 中,并且本库沿用 Axios 库现有的用法和特性,使其更加适合鸿蒙项目的开发。

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

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

axios 网络请求库的使用

接口列表

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

属性列表

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

下载安装

方式一:在 Terminal 窗口中,执行如下命令安装第三方包:

深色代码主题
复制
ohpm install [@ohos](/user/ohos)/axios

如果提示无 ohpm 命令,则是环境变量没有配置。可以参考文档进行配置。

方式二:在工程的 oh-package.json5 中设置第三方包依赖:

深色代码主题
复制
{
  "dependencies": {
    "[@ohos](/user/ohos)/axios": "^2.1.0"
  }
}

开通权限

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

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

简单使用

深色代码主题
复制
import axios from '[@ohos](/user/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 模块封装

深色代码主题
复制
// AxiosHttp.ets

import axios, { AxiosInstance, AxiosRequestConfig, AxiosRequestHeaders, AxiosResponse, InternalAxiosRequestConfig } from @ohos/axios”; import { LogUtils } from ‘…/utils/LogUtils’;

/**

  • 定义接口响应包装类 */ export interface BaseResponse { errorCode: number; errorMsg: string; }

/**

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

/**

  • 封装后,不支持传入拦截器
  • 需要自己定义接口继承 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( this.interceptorHooks?.requestInterceptor, this.interceptorHooks?.requestInterceptorCatch, ); this.instance.interceptors.response.use( this.interceptorHooks?.responseInterceptor, this.interceptorHooks?.responseInterceptorCatch, ); }

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;

  <span class="hljs-comment">// 403 token 过期</span>
  <span class="hljs-comment">// 登录过期对用户进行提示</span>
  <span class="hljs-comment">// 清除本地 token 和清空 Storage 中 token 对象</span>
  <span class="hljs-comment">// 跳转登录页面</span>
  <span class="hljs-keyword">case</span> <span class="hljs-number">403</span>:
    <span class="hljs-title function_">showToast</span>(<span class="hljs-string">"登录过期,请重新登录"</span>);
    <span class="hljs-comment">// 清除 token</span>
    <span class="hljs-comment">// StorageUtils.remove(StorageKeys.USER_TOKEN);</span>
    <span class="hljs-keyword">break</span>;

  <span class="hljs-comment">// 404 请求不存在</span>
  <span class="hljs-keyword">case</span> <span class="hljs-number">404</span>:
    <span class="hljs-title function_">showToast</span>(<span class="hljs-string">"网络请求不存在"</span>);
    <span class="hljs-keyword">break</span>;

  <span class="hljs-comment">// 其他错误,直接抛出错误提示</span>
  <span class="hljs-attr">default</span>:
    <span class="hljs-title function_">showToast</span>(error.<span class="hljs-property">response</span>.<span class="hljs-property">data</span>.<span class="hljs-property">message</span>);
}

} }

export default AxiosHttpRequest;

客户端封装

深色代码主题
复制
// 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 请求方法:’, <span class="hljs-subst">${config.method}</span>); LogUtils.debug(‘网络请求 Request 请求链接:’, <span class="hljs-subst">${config.url}</span>); LogUtils.debug(‘网络请求 Request Params:’, \n<span class="hljs-subst">${JsonUtils.stringify(config.params)}</span>); LogUtils.debug(‘网络请求 Request Data:’, <span class="hljs-subst">${JsonUtils.stringify(config.data)}</span>);

    axiosClient.config.showLoading = config.showLoading; if (config.showLoading) { showLoadingDialog(“加载中…”); }

    if (config.checkLoginState) { let hasLogin = await StorageUtils.get(StorageKeys.USER_LOGIN, false); LogUtils.debug(‘网络请求 Request 登录状态校验>>>’, <span class="hljs-subst">${hasLogin.toString()}</span>); if (hasLogin) { return config; } else { if (config.needJumpToLogin) { Router.push(RoutePath.LoginPage); } 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<span class="hljs-subst">${JsonUtils.stringify(response.data)}</span>); 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;

封装后使用

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

深色代码主题
复制
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](/user/ohos)/net.http 介绍

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

文档中心:HTTP 数据请求

官方简易封装

官方 [@ohos](/user/ohos)/net.http 模块直接使用起来不太友好,简易封装如下:

深色代码主题
复制
// http.ets

import http from @ohos.net.http’;

/**

  • 定义接口响应包装类 */ export interface BaseResponse { errorCode: number; errorMsg: string; }

/**

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

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 方法主动销毁 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 }); }

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

export default HttpBuilder;

官方 http 模块封装使用

深色代码主题
复制
import http from '[@ohos](/user/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 } }); }

写在最后

如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:

  1. 点赞和转发:点赞和评论是博主创作的动力。
  2. 关注博主:关注博主可以期待后续文章,不定期分享原创知识。
  3. 获取更多资料:想要获取更多完整鸿蒙最新 VIP 学习资料,请关注猫哥公众号【猫青年】,回复“鸿蒙”获取。

其他资源

</markdown>

更多关于HarmonyOS 鸿蒙Next在应用开发中使用 Axios 封装网络请求的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html

1 回复

更多关于HarmonyOS 鸿蒙Next在应用开发中使用 Axios 封装网络请求的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙Next应用开发中,若需使用Axios封装网络请求,需注意以下几点:

Axios本身是一个基于Promise的HTTP客户端,主要用于浏览器和Node.js环境。在鸿蒙Next这种原生应用环境中,虽然不能直接使用Axios,但可以通过类似的功能模块实现网络请求。

鸿蒙Next应用开发通常使用ArkTS(Ark TypeScript)语言,它提供了访问原生模块的能力。为了封装网络请求,你可以使用鸿蒙提供的网络模块,如fetch API或xhr(XMLHttpRequest)。这些API可以在ArkTS中直接使用,以实现类似于Axios的功能。

例如,使用fetch API封装一个简单的网络请求:

import fetch from '@ohos.network.fetch';

async function sendRequest(url: string, options?: RequestInit): Promise<any> {
    try {
        let response = await fetch(url, options);
        let data = await response.json();
        return data;
    } catch (error) {
        console.error("Error:", error);
    }
}

上述代码定义了一个sendRequest函数,它接受URL和可选的请求配置,并返回解析后的JSON数据。

如果需要在鸿蒙Next中实现更复杂的网络请求逻辑,可以进一步封装上述基本功能,包括错误处理、请求重试、超时设置等。

如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html

回到顶部