HarmonyOS 鸿蒙Next在应用开发中使用 Axios 封装网络请求
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
});
}
/**
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;
}
/**
写在最后
如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
- 点赞和转发:点赞和评论是博主创作的动力。
- 关注博主:关注博主可以期待后续文章,不定期分享原创知识。
- 获取更多资料:想要获取更多完整鸿蒙最新 VIP 学习资料,请关注猫哥公众号【猫青年】,回复“鸿蒙”获取。
其他资源
</markdown>更多关于HarmonyOS 鸿蒙Next在应用开发中使用 Axios 封装网络请求的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
更多关于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