封装uni.request api为类似axios的用法
前言
uniapp的请求都是通过uni.request
的api实现的,但是这个api和前端常用的axios库用法上有着太多的不同了,移植起来非常痛苦,所以萌生了自己造一个轮子的想法,由于本人技术菜鸡,只能浅浅的仿照一个了。
实际上本人喜欢axios的config的方式调用请求,如下:
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
});
所以大体上都是围绕这种用法展开工作的。
首先说下使用上的注意事项:
- get请求不支持使用data字段,用了估计也会被忽略,get请求请务必使用params字段传参,参考axios的params
- create方法创建实例的时候,支持的默认配置只有三个字段:
baseURL、timeout、headers
- 为了实现promise返回结果,不允许任何调用的请求配置中存在:
success、fail、complete
回调函数字段,本人通过ts美滋滋的屏蔽了,你用了肯定会报错警告。 - 请求前的拦截器只有成功的拦截器,因为axios中的请求前错误拦截器测试根本没有可以触发的地方,所以直接割了这个。
- 只有响应后的拦截器允许
throw
抛出错误和Promise.reject()
。
基本就这样了,具体可以看看代码,都有注释(优秀作风)
使用
这里是业务层的封装,创建MyAxios实例,配置默认数据baseUrl这些,添加拦截器,请求前对配置对象做特殊处理,请求后解包,报错toast提示。
由于class类的方式,不能将类直接作为函数调用,所以最后提取了api.request
方法单独导出了
import MyAxios, { METHODS } from "./myAxios";
/** 第一层数据类型 */
export interface ResponseData {
code: Number;
data: Object;
msg: string;
}
const api = MyAxios.create({
baseURL: "https://xxx",
timeout: 1000,
});
/** 请求前拦截器 */
api.useRequestInterceptor((config) => {
return config;
});
/** 请求后拦截器 */
api.useResponseInterceptor<ResponseData>(
(res) => {
//如果code不等于1则说明有错误
const { code, msg } = res;
if (code !== 1) {
uni.showToast({
title: `网络请求失败`,
icon: "error",
});
return Promise.reject(new Error(msg));
}
//解包处理
const data = res.data;
return data;
},
(error) => {
uni.showToast({
title: `网络请求失败`,
icon: "error",
});
return error;
}
);
/** 提取request请求函数 */
const request = api.request.bind(api);
export { request, METHODS };
export default api;
api层的使用:
import { request, METHODS } from "@/request";
/** 测试接口 */
export function test() {
return request({
url: "/test",
method: METHODS.GET,
params: {
name: "我是测试接口",
},
});
}
request方法支持泛型,传入一个泛型后就可以推导出本次请求结果的数据类型,方便ts使用了。
import { request, METHODS } from "@/request";
/** 测试接口 */
export function test() {
return request<string>({ //这里的string就是响应拦截器中res.data解包之后的值了
url: "/test",
method: METHODS.GET,
params: {
name: "我是测试接口",
},
});
}
具体的业务使用:
import { test } from "@/apis/test";
test()
.then((res) => {
console.log(res);
})
.catch((error) => {
console.log(error);
});
MyAxios封装
/** 请求方法常量 */
export enum METHODS {
"OPTIONS" = "OPTIONS",
"GET" = "GET",
"HEAD" = "HEAD",
"POST" = "POST",
"PUT" = "PUT",
"DELETE" = "DELETE",
"TRACE" = "TRACE",
"CONNECT" = "CONNECT",
}
/** 头信息 */
export interface Headers {
[key: string]: string | number | boolean | undefined | null;
}
/** 实例化的配置 */
export interface AxiosConfig {
baseURL?: string;
timeout?: number;
headers?: Headers;
}
/** 默认配置 */
const defaultConfig: AxiosConfig = {
baseURL: "",
timeout: 60000,
headers: {},
};
/** 定制的请求参数 */
export interface RequestOptions extends Omit<UniNamespace.RequestOptions, "success" | "fail" | "complete"> {}
/** 已知请求类型的配置 */
export interface KnownConfig extends Omit<RequestOptions, "method"> {}
/** get请求类型配置 */
export interface GetConfig extends Omit<KnownConfig, "data"> {
params?: Record<string, any>;
}
/** post请求类型配置 */
export interface PostConfig extends KnownConfig {
params?: Record<string, any>;
}
/** request请求类型配置 */
export interface RequestConfig extends RequestOptions {
params?: Record<string, any>;
}
/** 错误对象 */
export interface AxiosError extends Error {
options?: RequestOptions;
}
/** 请求前拦截器 */
export type RequestInterceptorFn = (config: RequestOptions) => RequestOptions;
export type RequestInterceptorArray = Array<RequestInterceptorFn>;
/** 请求成功拦截器 */
export type ResponseSuccessInterceptorFn<T = any> = (result: T) => any;
export type ResponseSuccessInterceptorArray<T> = Array<ResponseSuccessInterceptorFn<T>>;
/** 请求失败拦截器 */
export type ResponseInterceptorFailFn = (error: AxiosError | Error) => AxiosError | Error;
export type ResponseInterceptorFailArray = Array<ResponseInterceptorFailFn>;
class Axios {
/** createOptions */
private createOptions: AxiosConfig;
/** 取消请求函数存储器 */
private cancelTokenMap: Map<string, Function> = new Map();
/** 请求前拦截器 */
private requestInterceptors: RequestInterceptorArray = [];
/** 请求后成功的拦截器 */
private responseSuccessInterceptors: ResponseSuccessInterceptorArray<any> = [];
/** 请求后失败的拦截器 */
private responseFailInterceptors: ResponseInterceptorFailArray = [];
constructor(options: AxiosConfig = defaultConfig) {
const { baseURL, timeout, headers } = options;
this.createOptions = {
baseURL: baseURL ?? defaultConfig.baseURL,
timeout: timeout ?? defaultConfig.timeout,
headers: headers ?? defaultConfig.headers,
};
}
/** 静态创建实例方法 */
public static create(options?: AxiosConfig): Axios {
return new Axios(options);
}
/** get请求 */
public get<T = any>(options: GetConfig): Promise<T> {
const getOptions: KnownConfig = { ...options };
if (options.params) {
getOptions.data = options.params;
Reflect.deleteProperty(getOptions, "params");
}
const requestConfig = this.mergeDefaultConfig(getOptions);
return this._request<T>({ method: METHODS.GET, ...requestConfig });
}
/** post请求 */
public post<T = any>(options: PostConfig): Promise<T> {
let postOptions: KnownConfig = { ...options };
//生成参数字符串
const params = options.params;
let paramsStr = "";
if (params) {
Object.keys(params)
.sort((a, b) => a.localeCompare(b))
.forEach((key) => {
paramsStr += `${key}=${params[key]}&`;
});
paramsStr = paramsStr.replace(/&$/, "");
}
//拼接参数字符串
if (postOptions.url.includes("?")) {
postOptions.url += `&${paramsStr}`;
} else {
postOptions.url += `?${paramsStr}`;
}
const requestConfig = this.mergeDefaultConfig(postOptions);
return this._request<T>({ method: METHODS.POST, ...requestConfig });
}
/** 自动判断get和post请求的请求方法 */
public request<T = any>(options: RequestConfig): Promise<T> {
let { method } = options;
if (!method) method = METHODS.GET;
switch (method) {
case METHODS.GET:
return this.get<T>(options);
case METHODS.POST:
return this.post<T>(options);
default:
return Promise.reject(new Error("不支持的请求方法"));
}
}
/** 获取uni的请求并类型转换为Promise */
private _request<T = any>(options: RequestConfig): Promise<T> {
return new Promise<T>((resolve, reject) => {
//请求前拦截器
let requestConfig = options;
this.requestInterceptors.forEach((interceptor) => {
requestConfig = interceptor(requestConfig);
});
const requestTask = uni.request({
...requestConfig,
success: async (res) => {
try {
const { statusCode } = res;
//错误处理
if (statusCode !== 200) {
let msg = "请求失败";
const data = res.data as { msg: string };
if (typeof data.msg === "string") msg = data.msg;
let error: AxiosError = this.createError(msg, options);
//删除取消函数
this.cancelTokenMap.delete(options.url);
//请求失败拦截器
for (let i = 0, len = this.responseFailInterceptors.length; i < len; i++) {
error = this.responseFailInterceptors[i](error);
}
return reject(error);
}
//删除取消函数
this.cancelTokenMap.delete(options.url);
//请求成功拦截器
let resultData = res.data;
for (let i = 0, len = this.responseSuccessInterceptors.length; i < len; i++) {
resultData = await this.responseSuccessInterceptors[i](resultData);
}
return resolve(resultData as T);
} catch (err: any) {
let msg = "请求失败";
if (typeof err.errMsg === "string") msg = err.errMsg;
const error: AxiosError = this.createError(msg, options);
//删除取消函数
this.cancelTokenMap.delete(options.url);
//请求失败拦截器
for (let i = 0, len = this.responseFailInterceptors.length; i < len; i++) {
err = this.responseFailInterceptors[i](err);
}
return reject(err);
}
},
fail: (err) => {
let msg = "请求失败";
if (typeof err.errMsg === "string") msg = err.errMsg;
let error: AxiosError = this.createError(msg, options);
//删除取消函数
this.cancelTokenMap.delete(options.url);
//请求失败拦截器
for (let i = 0, len = this.responseFailInterceptors.length; i < len; i++) {
error = this.responseFailInterceptors[i](error);
}
return reject(error);
},
});
//存储取消函数
this.cancelTokenMap.set(options.url, requestTask.abort.bind(requestTask));
});
}
/** 获取用户配置和默认配置的合并后的配置对象 */
private mergeDefaultConfig(options: KnownConfig): RequestOptions {
const { baseURL, timeout, headers } = this.createOptions;
const url = `${baseURL}${options.url ?? ""}`;
const mergeOptions = { timeout, ...options };
Reflect.deleteProperty(mergeOptions, "url");
mergeOptions.url = url;
const oh = options.header;
if (oh) {
mergeOptions.header = { ...headers, ...oh };
} else {
mergeOptions.header = headers || {};
}
return mergeOptions;
}
/** 创建错误对象 */
private createError(msg: string, options: RequestOptions): AxiosError {
const error: AxiosError = new Error(msg);
error.options = options;
return error;
}
/** 通过url获取取消请求函数 */
public getRequestAbort(url: string): Function | undefined {
return this.cancelTokenMap.get(url);
}
/** 注册请求前的拦截器 */
public useRequestInterceptor(fn: RequestInterceptorFn): void {
this.requestInterceptors.push(fn);
}
/** 注册请求后的拦截器 */
public useResponseInterceptor<T>(successFn: ResponseSuccessInterceptorFn<T>, failFn?: ResponseInterceptorFailFn): void {
this.responseSuccessInterceptors.push(successFn);
failFn && this.responseFailInterceptors.push(failFn);
}
}
export default Axios;
取消请求
其实我把取消请求功能也加上了,通过实例api.getRequestAbort(url)
方法,但是个人感觉不是很好使,这个url是一个完整链接哦,是包含baseUrl和链接参数的,因为实在想不到啥好办法去区分了。
如果你需要的话,可以自行使用,getRequestAbort
得到的就是abort
取消请求函数,运行它即可。
需要注意的是,如果这个请求已经完成了,success或者fail,那个getRequestAbort就获取不到了,因为我把abort
清理了。
分类:
UNI-APP
标签:
axios拦截器取消请求uni.request
版权申明
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿站点。未经许可,禁止转载。
全部评论 1
a
Google Chrome Windows 10