HarmonyOS NEXT应用开发实战之axios网络库的封装和使用
HarmonyOS NEXT应用开发实战之Axios网络库的封装和使用 在现代应用开发中,网络请求是不可或缺的一部分。Axios库非常流行,而在HarmonyOS NEXT中,Axios库依然可用,可使用Axios库进行网络请求,可以极大地简化开发者的工作。通过对Axios的封装,不仅提升了代码的可维护性和可读性,还提供了一系列便捷的功能。本文将介绍Axios网络库的封装及其优点,帮助开发者在Ha
HarmonyOS NEXT应用开发实战之Axios网络库的封装和使用
在现代应用开发中,网络请求是不可或缺的一部分。Axios库非常流行,而在HarmonyOS NEXT中,Axios库依然可用,可使用Axios库进行网络请求,可以极大地简化开发者的工作。通过对Axios的封装,不仅提升了代码的可维护性和可读性,还提供了一系列便捷的功能。本文将介绍Axios网络库的封装及其优点,帮助开发者在HarmonyOS NEXT环境下实现高效的网络请求。
axios三方库介绍
@ohos/axios是基于Axios原库v1.3.4版本为HarmonyOS/OpenHarmony适配的三方库,是一个基于promise的网络请求库,可以运行node.js和浏览器中,沿用其现有用法和特性。
axios三方库安装
在项目的根目录下,命令行执行以下命令即可:
ohpm install @ohos/axios
axios三方库封装的意义
对axios进行封装的意义在于提供更高层次的抽象,以便简化网络请求的使用和管理。以下是一些具体的理由:
1.统一接口:封装后,可以统一管理所有的网络请求接口,使得在应用中调用网络请求时更加一致,减少重复代码。
2.简化配置:封装可以避免每次请求都需要重复配置相似的参数(例如headers、请求方式等),通过配置对象直接传入更简洁。
3.请求和响应拦截器:封装允许在发送请求之前或收到响应之后,对请求或响应进行处理,比如添加公共的请求头、处理错误、数据格式化等。
4.错误处理:通过自定义的错误处理机制,可以实现统一的错误处理逻辑,比如根据状态码处理特定的错误(例如401未登录、403权限不足等)。
5.增强功能:可以根据项目需求添加额外的功能,例如显示加载状态、处理用户登录状态等。
6.提高可维护性:将网络请求相关的逻辑集中管理,可以让代码更加清晰,降低维护成本。
7.支持特定业务需求:可根据实际的业务需求扩展功能,比如提供缓存机制、重试机制等,增强请求的灵活性。
封装后的使用效果
//axiosClient.ets
//author:csdn猫哥(blog.csdn.net/qq8864)
import {axiosClient,HttpPromise} from '../../utils/axiosClient';
import { ZhiNewsRespData,ZhiDetailRespData, HotMovieReq, MovieRespData } from '../bean/ApiTypes';
// 获取知乎列表页api接口
export const getZhiHuNews = (date:string): HttpPromise<ZhiNewsRespData> => axiosClient.get({url:'/zhihunews/'+date});
// 获取知乎详情页api接口
export const getZhiHuDetail = (id:string): HttpPromise<ZhiDetailRespData> => axiosClient.get({url:'/zhihudetail/'+id});
// 获取热门影视接口,仅作为post的使用示例,未使用
export const getHotMovie = (req:HotMovieReq): HttpPromise<MovieRespData> => axiosClient.post({url:'/hotmovie',data:req});
// 使用举例:
/*
getHotMovie({start:1,count:2,city:'郑州'}).then((res) => {
Log.debug(res.data.message)
Log.debug("request","res.data.code:%{public}d",res.data.code)
}).catch((err: BusinessError) => {
Log.debug("request","err.data.code:%d",err.code)
Log.debug("request",err.message)
});
*/
可以看出,封装后接口的使用清晰简单直观,一行代码一个接口,风格统一,参数简单整洁。
封装的实现过程
//axiosClient.ets
//author:csdn猫哥(blog.csdn.net/qq8864)
import axios, { AxiosError, AxiosInstance, AxiosRequestHeaders, AxiosResponse, InternalAxiosRequestConfig } from "@ohos/axios";
interface HttpResponse<T> {
data: T;
status: number;
statusText: string;
config: HttpRequestConfig;
}
export type HttpPromise<T> = Promise<HttpResponse<T>>;
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;
errorHandler?: (error: any) => void; // 错误处理
}
export 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): HttpPromise<T> {
return new Promise<HttpResponse<T>>((resolve, reject) => {
this.instance.request<any, HttpResponse<T>>(config)
.then(res => {
resolve(res);
})
.catch(err => {
const errorHandler = config.errorHandler || errorHandlerDefault;
errorHandler(err);
reject(err);
});
});
}
get<T = any>(config: HttpRequestConfig): HttpPromise<T> {
return this.request({ ...config, method: 'GET' });
}
post<T = any>(config: HttpRequestConfig): HttpPromise<T> {
return this.request({ ...config, method: 'POST' });
}
delete<T = any>(config: HttpRequestConfig): HttpPromise<T> {
return this.request({ ...config, method: 'DELETE' });
}
patch<T = any>(config: HttpRequestConfig): HttpPromise<T> {
return this.request({ ...config, method: 'PATCH' });
}
}
function errorHandlerDefault(error: any) {
if (error instanceof AxiosError) {
// 处理Axios的错误
}
// 处理其他类型的错误
}
export default AxiosHttpRequest;
如何使用?
上述封装仅是统一了接口访问风格,但还不方便统一使用,可以再建一个全局单例的工具类使用:
//axiosClient.ets
//author:csdn猫哥(blog.csdn.net/qq8864)
import {AxiosHttpRequest,HttpPromise} from './axiosHttp'
import {AxiosRequestHeaders,AxiosError } from '@ohos/axios';
import { Log } from './logutil';
import { promptAction } from "@kit.ArkUI";
function showToast(msg:string){
Log.debug(msg)
promptAction.showToast({ message: msg })
}
function showLoadingDialog(msg:string){
Log.debug(msg)
promptAction.showToast({ message: msg })
}
function hideLoadingDialog() {
}
/**
* axios请求客户端创建
*/
const axiosClient = new AxiosHttpRequest({
baseURL: "http://175.178.126.10:8000/api/v1",
timeout: 10 * 1000,
checkResultCode: false,
showLoading:true,
headers: {
'Content-Type': 'application/json'
} as AxiosRequestHeaders,
interceptorHooks: {
requestInterceptor: async (config) => {
// 在发送请求之前做一些处理,例如打印请求信息
Log.debug('网络请求Request 请求方法:', `${config.method}`);
Log.debug('网络请求Request 请求链接:', `${config.url}`);
Log.debug('网络请求Request Params:', `\n${JSON.stringify(config.params)}`);
Log.debug('网络请求Request Data:', `${JSON.stringify(config.data)}`);
axiosClient.config.showLoading = config.showLoading
if (config.showLoading) {
showLoadingDialog("加载中...")
}
if (config.checkLoginState) {
//let hasLogin = await StorageUtils.get(StorageKeys.USER_LOGIN, false)
//Log.debug('网络请求Request 登录状态校验>>>', `${hasLogin.toString()}`);
// if (hasLogin) {
// return config
// } else {
// if (config.needJumpToLogin) {
// //Router.push(RoutePath.TestPage)
// }
// throw new AxiosError("请登录")
// }
}
return config;
},
requestInterceptorCatch: (err) => {
Log.error("网络请求RequestError", err.toString())
if (axiosClient.config.showLoading) {
hideLoadingDialog()
}
return err;
},
responseInterceptor: (response) => {
//优先执行自己的请求响应拦截器,在执行通用请求request的
if (axiosClient.config.showLoading) {
hideLoadingDialog()
}
Log.debug('网络请求响应Response:', `\n${JSON.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);
} else {
return Promise.reject(response);
}
},
responseInterceptorCatch: (error) => {
if (axiosClient.config.showLoading) {
hideLoadingDialog()
}
Log.error("网络请求响应异常", error.toString());
errorHandler(error);
return Promise.reject(error);
},
}
});
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 {axiosClient,HttpPromise};
封装后的使用
import {axiosClient,HttpPromise} from '../../utils/axiosClient';
import { ZhiNewsRespData,ZhiDetailRespData, HotMovieReq, MovieRespData } from '../bean/ApiTypes';
// 获取知乎列表页api接口
export const getZhiHuNews = (date:string): HttpPromise<ZhiNewsRespData> => axiosClient.get({url:'/zhihunews/'+date});
// 获取知乎详情页api接口
export const getZhiHuDetail = (id:string): HttpPromise<ZhiDetailRespData> => axiosClient.get({url:'/zhihudetail/'+id});
// 获取热门影视接口,仅作为post的使用示例,未使用
export const getHotMovie = (req:HotMovieReq): HttpPromise<MovieRespData> => axiosClient.post({url:'/hotmovie',data:req});
// 使用举例:
/*
getHotMovie({start:1,count:2,city:'郑州'}).then((res) => {
Log.debug(res.data.message)
Log.debug("request","res.data.code:%{public}d",res.data.code)
}).catch((err: BusinessError) => {
Log.debug("request","err.data.code:%d",err.code)
Log.debug("request",err.message)
});
*/
一行代码写好一个接口,清晰直观。但是接口相关的包体还是需要定义好的:
//===知乎日报接口包体定义
export interface ZhiNewsItem {
id:string;
image:string;
title:string;
url:string;
hint:string;
date: string;
isShowDivider?: boolean;
}
export interface ZhiNewsRespData {
code: number;
message: string;
stories: Array<ZhiNewsItem>;
top_stories: Array<ZhiNewsItem>;
date: string;
}
export type ZhiDetailItem={
types:string;
value:string;
}
export interface ZhiDetailRespData {
code: number;
message: string;
content: Array<ZhiDetailItem>;
title: string;
author: string;
bio: string;
avatar: string;
image: string;
more: string;
}
总结
通过对Axios的封装,我们可以在HarmonyOS NEXT应用开发中实现更高效、更整洁的网络请求处理。封装不仅提升了代码的可维护性和可读性,还提供了简洁、一致的使用体验。无论你是初学者还是经验丰富的开发者,都可以通过这种封装方式大幅度提高开发效率。在未来的开发中,我们推荐您使用这种封装技术来处理网络请求,以便更好地适应快速发展的移动应用开发需求。
更多推荐
所有评论(0)