@itshixun/qst-request-lib
QST接口请求工具库
1. 常用类型和枚举定义:
- QstResult: 约定的数据响应类型
/** 约定的常规接口返回数据结构体 */ export interface QstResult<T> { /** http状态码 3位数 比如200、401、500等 */ status: number; /** 扩展状态码 7位数 比如2000000 4010000 4000297 5000000等 */ code: number; /** 成功标识 */ success?: boolean; /** 消息 */ message?: string; /** 返回的数据 */ data?: T; /** 额外的返回数据,比如用户中心的extras.failureCount,返回登录错误的次数 */ extras?: Record<string, unknown>; }
- QstPagination: 约定的分页数据类型
/** 约定的分页数据结构 */ export interface QstPagination<T> { /** 总条数 */ total: number; offset: number; limit: number; /** 当前页码 */ pageNumber: number; /** 每夜条数 */ pageSize: number; /** 数据数组 */ rows: T[]; }
- ContentTypeEnum: ContentType类型枚举
/** request contentType */ export enum ContentTypeEnum { /** json */ JSON = 'application/json;charset=UTF-8', /** form-data qs */ FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8', /** form-data upload */ FORM_DATA = 'multipart/form-data;charset=UTF-8', }
2. aioxs请求封装方法:requestWrapper
封装常规axios请求方法:requestWrapper:提炼通用的响应处理和错误提示,剥离AxiosResponse<T>封装并返回T类型的数据,业务端通常不需要再进行Promise.catch,仅需要处理必要的响应结果,除非在处理响应结果时有可能抛出业务逻辑相关错误时,再通过Promise.catch进行处理。
2.1 选项设置
在使用requestWrapper方法前,首先要进行选项设置。请求封装方法的选项类型RequestWrapperOption定义如下:
/** 请求封装方法的选项类型定义 */
export interface RequestWrapperOption<T = any> {
/**
* 自定义错误处理方法,仅在需要特殊处理的接口请求时使用此方法,作为requestWrapper中默认错误处理方法的补充
* 要求保持返回的错误类型仍为AxiosError,保证错误信息在Promise链中的延续和可追溯
*/
resolveError?: (err: AxiosError<T>) => AxiosError<T>;
/** 显示错误消息提示的方法 */
handleMessage?: (msg: string, err?: AxiosError<T>) => void;
/** 错误已处理标志位,默认AxiosError.code === 'handled'时,表示该错误已处理,后续错误处理中判断code==='handled'时,不再弹窗错误提示,避免重复弹出 */
handledCode?: string;
/** 处理401(需要登录)的方法 */
handle401?: (err: AxiosError<T>) => void;
/** 处理403(无权限需要登录)的方法 */
handle403?: (err: AxiosError<T>) => void;
/** aixos实例,使用qstRequest的get/post等便捷请求别名时,需要传入该实例,否者默认使用aixos静态实例 */
axiosInstance?: AxiosInstance;
}
我们可以通过setDefaultRequestWrapperOption设置requestWrapper方法的全局默认选项,比如:
setDefaultRequestWrapperOption({
/** 使用element-plus的Message组件弹出错误提示消息 */
handleMessage: (msg: string) => ElMessage.error(msg),
/** 默认error.code 为 handled 时,表示错误已处理,不需要再弹出提示 */
handledCode: 'handled',
/** 401时跳转到登录页 */
handle401: () => router.replace({ name: 'login' }),
/** 403时无权限,跳转到登录页 */
handle403: () => {
ElMessage.error('当前登录角色无权限,请重新登录');
logout();
router.replace({ name: 'login' });
},
/** 常规的请求错误在requestWrapper中已做处理,一般不需要此处自定义resolveError方法,这里可以不做设置 */
resolveError: (err: AxiosError<unknown>) => err,
/** 设置qstRequest使用的默认axios实例 */
axiosInstance: mainInstance,
});
2.2 调用方法
一个常规的axios请求流程,大致如下:
/** axios请求示例 */
axios
.get<QstResult<ResData>>('/api/xxx')
.then((res: AxiosResponse<QstResult<ResData>>) => {
// 处理返回数据,取得res.data.data
const data = getResult(res);
return data;
})
.catch((err: AxiosError<QstResult<ResData>>) => {
// 处理错误信息,弹出错误消息
handleError(err);
return Promise.reject(err);
});
我们通过requestWrapper方法,提炼通用的响应处理和错误提示,剥离AxiosResponse<T>封装并返回T类型的数据,业务端通常不需要再进行Promise.catch,仅需要处理必要的响应结果,除非在处理响应结果时有可能抛出业务逻辑相关错误时,才通过Promise.catch进行处理。上面的方法,使用requestWrapper方法的示例如下:
/** 使用requestWrapper封装请求,此处使用默认设置,不再传入RequestWrapperOption选项 */
requestWrapper<QstResult<ResData>>(() => axios.get('/api/xxx')).then((res: QstResult<ResData>) => {
return res.data;
});
上面的例子,经过requestWrapper处理后,Promise.then可以直接取得剥离AxiosResponse封装的响应数据。同时所有的常见请求错误,都已经在requestWrapper内部做了处理,如果确定Promise.then中不会抛出业务相关的错误,就不需要再使用Promise.catch进行错误处理了。
针对具体的接口,我们也可以传入专门针对该接口的RequestWrapperOption选项,从而使不同的接口可以获得各自不同的处理方式。比如在请求登录接口时,如果返回401说明登录接口本身的后端逻辑出错了,我们给出一个服务端错误的提示;另外我们希望登录接口使用原生alert的方式提醒用户。我们可以像下面这样,调用requestWrapper方法时传入RequestWrapperOption选项:
requestWrapper<QstResult<ResData>>(
() => axios.post('/api/login', { loginData }),
// 传入RequestWrapperOption,会通过Object.assign合并默认的RequestWrapperOption
{
handle401: () => alert('服务端错误:登录接口返回401!'),
handleMessage: (msg: string) => alert(msg),
}
).then((res) => {
// 保存登录token
setToken(res.data.accessToken);
// 登录成功的后续处理...
});
通过setDefaultRequestWrapperOption设置全局选项,并且具体接口也可以在调用requestWrapper时传入不同的配置选项,可以实现:大部分接口请求使用统一的错误处理/错误提醒/401登录跳转等方法的同时,部分特殊接口使用自定义的错误处理/错误提醒/401状态处理方法。
3 仿aixos的请求别名
基于requestWrapper方法,我们构建了qstRequest对象,进一步封装了请求别名(get/post等)方法。我们可以像使用axios的便捷请求别名方法一样,以更简洁和符合使用习惯的方式进行接口请求。比如上面2.2 调用方法的示例:
/** 使用requestWrapper封装请求,此处使用默认设置,不传入RequestWrapperOption选项 */
requestWrapper<QstResult<ResData>>(() => axios.get('/api/xxx'));
我们也可以使用便捷请求别名进行请求,如下所示:
/** 使用qstRequest.get请求别名,此处使用默认设置,不传入RequestWrapperOption选项 */
qstRequest.get<QstResult<ResData>>('/api/xxx');
上述示例使用全局设置的默认RequestWrapperOption选项。如果某个特定接口使用另外的aixos实例进行请求,我们可以如下例这样,传入RequestWrapperOption选项,针对具体请求进行特殊配置:
qstRequest.post<QstResult>('/api/xxx/save', data, config, {
// 针对本接口,使用名为"mySaveInstance"的axios实例进行请求
axiosInstance: mySaveInstance,
// 配置本接口不弹出错误提示消息,仅在控制台打印错误信息
handleMessage: (msg: string) => {
console.error(msg);
},
});
注意
:axios中AxiosInstance的定义,在v1.5.x和1.6.x中不同,因此如果您使用typescript开发,在配置RequestWrapperOption的axiosInstance时,需要安装最新的axios版本(目前 1.6.1),才可保证ts类型正确。
qstRequest的请求方式别名列表
qstRequest: {
get<T = any, D = any>(url: string, config?: AxiosRequestConfig<D>, option?: RequestWrapperOption<T>): Promise<T>;
delete<T = any, D = any>(url: string, config?: AxiosRequestConfig<D>, option?: RequestWrapperOption<T>): Promise<T>;
post<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig, option?: RequestWrapperOption<T>): Promise<T>;
put<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig, option?: RequestWrapperOption<T>): Promise<T>;
patch<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig, option?: RequestWrapperOption<T>): Promise<T>;
postForm<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig, option?: RequestWrapperOption<T>): Promise<T>;
putForm<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig, option?: RequestWrapperOption<T>): Promise<T>;
patchForm<T = any, D = any>(url: string, data?: D, config?: AxiosRequestConfig, option?: RequestWrapperOption<T>): Promise<T>;
}
4 取得Axios原始响应数据AxiosResponse
上述工具(requestWrapper和qstRequest)帮助我们剥离AxiosResponse结构,直接返回T类型的数据,方便进行后续的数据处理;但是有些特殊情况下需要获取完整的AxiosResponse数据,比如要获取 response headers 中某个字段的值。这里我们通过requestWrapperRaw的方法,或者qstRequestRaw对象的get/post等请求别名,来获取Axios的原始响应数据:
/** 使用requestWrapperRaw封装请求,返回原始的Axios响应数据,即AxiosResponse<ResponseData>类型的数据 */
requestWrapperRaw<ResponseData>(() => axios.get('/api/xxx'));
/** 同样,使用qstRequestRaw下定义的请求方法别名,返回原始Axios响应数据 */
qstRequestRaw.get<ResponseData>('/api/xxx');