开始
上篇写了封装鸿蒙自带的http网络请求库,大量jy们在群里和评论区反馈说要封装个基于axios的,接下来我们一步一个脚印来实现和进行终极封装!
开发环境
-
Windows
-
DevEco Studio NEXT Developer Preview2
-
HarmonyOS next Developer Preview2
-
java version "11.0.18" 2023-01-17 LTS
-
hdc 1.2.0a
-
手机:Mate 60Pro (HarmonyOS NEXT Developer Preview2)
导入axios库
"@ohos/axios": "^2.1.1",
探讨封装
接下来我们要封装axios,我们想到的是后续怎么拓展,比方说后面我不用axios了,有个更强大的网络库来支持,类似安卓早期xutils、Volley、okhttp,如果要替换相当麻烦,所有我们需要封装一个AxiosClient(用来处理封装axios核心代码)还有一个AxiosHttp(用来处理网络请求拦截、实现、暴露方法供上层调用),这样一来分工明确,直接上代码
封装泛型工具类
和http一样需要封装一个封装泛型工具类,代码很简单
export class Result<T> {
code: number = 0
msg: string = ""
data: T | null = null
}
封装AxiosClient
- 接下来封装AxiosClient
/**
* 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("请登录","-100")
throw new Error("请登录")
}
}
return config;
},
requestInterceptorCatch: (err) => {
LogUtils.error("网络请求RequestError", err.toString())
if (axiosClient.config.showLoading) {
hideLoadingDialog()
}
return err;
},
responseInterceptor: (response: AxiosResponse) => {
//优先执行自己的请求响应拦截器,在执行通用请求request的
if (axiosClient.config.showLoading) {
hideLoadingDialog()
}
LogUtils.debug('网络请求响应Response:', `\n${JsonUtils.stringify(response.data)}`);
if (response.status === 200) {
let config = response.config as HttpRequestConfig
const checkResultCode = 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);
},
}
});
- errorHandler处理,一些40开头的错误
export function errorHandler(error: CommonType) {
if (error instanceof AxiosError) {
switch (error.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.message)
}
}
}
封装AxiosHttp
- 由于api11不支持any了,这边定义了一个通用数据类型CommonType来代替
//通用数据类型
export type CommonType = number | string | boolean | Array<number> | Array<string> | Array<boolean> | object | Object | ArrayBuffer
- 首先定义两个接口,用来处理响应码和拦截相关
interface InterceptorHooks {
requestInterceptor?: (config: HttpRequestConfig) => Promise<HttpRequestConfig>;
requestInterceptorCatch?: (error: CommonType) => CommonType;
responseInterceptor?: (response: AxiosResponse) => AxiosResponse | Promise<AxiosResponse>;
responseInterceptorCatch?: (error: CommonType) => CommonType;
}
export interface HttpRequestConfig extends InternalAxiosRequestConfig {
showLoading?: boolean; //是否展示请求loading
checkResultCode?: boolean; //是否检验响应结果码
checkLoginState?: boolean //校验用户登陆状态
needJumpToLogin?: boolean //是否需要跳转到登陆页面
interceptorHooks?: InterceptorHooks
}
- 接下来网络请求构造和接口封装
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是必传,所以在实现的子类我设置成非必传会报错,加了个忽略注解
this.interceptorHooks?.requestInterceptor,
this.interceptorHooks?.requestInterceptorCatch,
);
this.instance.interceptors.response.use(
this.interceptorHooks?.responseInterceptor,
this.interceptorHooks?.responseInterceptorCatch,
);
}
// 类型参数的作用,T决定AxiosResponse实例中data的类型
request<T = CommonType>(config: HttpRequestConfig): Promise<T> {
return new Promise<T>((resolve, reject) => {
this.instance
.request<CommonType, T>(config)
.then(res => {
resolve(res);
})
.catch((err: CommonType) => {
LogUtils.error("网络请求Request异常:", err.toString())
errorHandler(err)
if (err) {
reject(err);
}
});
});
}
get<T = CommonType>(config: HttpRequestConfig): Promise<T> {
config.method = 'GET'
return this.request(config);
}
post<T = CommonType>(config: HttpRequestConfig): Promise<T> {
config.method = 'POST'
return this.request(config);
}
delete<T = CommonType>(config: HttpRequestConfig): Promise<T> {
config.method = 'DELETE'
return this.request(config);
}
patch<T = CommonType>(config: HttpRequestConfig): Promise<T> {
config.method = 'PATCH'
return this.request(config);
}
}
使用
- 使用方式两种1、包一个Result,用来判断code码 2、直接返回数据
- commonHeader是自定义的
- 其中axios有个params和data传参,params是类似于安卓的query方式,data是类似于安卓的body方式
const commonHeader: AxiosHeaders = new AxiosHeaders()
/**
* 直接返回数据TestModel
* @returns
*/
export function getTestListAxios(date: string = "") {
return axiosClient.get<TestModel>({
url: baseUrl + "test",
params: { "date": date },
showLoading: true,
headers: commonHeader
})
}
/**
* 测试校验API data:T泛型数据
* @returns
*/
export function getTest() {
return axiosClient.get<Result<Test[]>>(
{
url: baseUrl + "test",
checkResultCode: true,
showLoading: true,
headers: commonHeader
}
)
}
总结
- 封装泛型工具类
- 封装AxiosClient
- 封装AxiosHttp
- 最后直接使用
- Axios有个好处就是不用解析,底层解析了,直接可以用想要的数据
下集预告
下一篇我们封装一个事件总线工具,类型安卓的eventBus用来刷新页面组件和传参
本文正在参加华为鸿蒙有奖征文征文活动