「这是我参与2022首次更文挑战的第34天,活动详情查看:2022首次更文挑战」
基于 axios 的封装
// axios实例
export const createInstatnce = ({
baseURL = '',
timeout = 30000,
headers = {}
} = {}) => {
const instance = axios.create({
baseURL,
timeout,
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/json',
headers
}
});
return instance;
};
通用的 get 和 post 生成器
export const commonGetGenerator = instance => {
return async (url, params = {}, options = {}) =>
instance
.get(url, {
params,
...options
})
.then(res => res.data);
};
export const commonPostGenerator = instance => {
return async (url, data, options) =>
instance.post(url, data, options).then(res => res.data);
};
拦截器
instance.interceptors.request.use(
config => {
// 自定义请求头
config.headers.common.Id = String(Math.random()).slice(2);
return {
...config,
data: config.data || {}
};
},
error => {
console.error('request error : ', error);
return Promise.reject(error);
}
);
instance.interceptors.response.use(
res => {
const {
data,
config: { url }
} = res;
// code非0业务逻辑处理
if (data && data.code) { }
return res;
},
error => {
notification.error({
message: `${error.config.url}:请求发生错误`,
description: `${error.message}\n ${error.stack} `
});
console.error('请求异常 : ', error);
return Promise.reject(error);
}
);
创建 get 和 post 请求
export const get = commonGetGenerator(instance);
export const post = commonPostGenerator(instance);
使用
post('/api/url', param)
.then(({ data = [] }) => {
setLoading(false);
setList(data);
})
.catch(err => {
setLoading(false);
});
取消重复请求
对请求进行“防抖”处理。这里主要是为了阻止用户在某些情况下短时间内重复点击某个按钮,导致前端向后端重复发送多次请求。
import axios from 'axios';
/* 创建axios实例 */
const service = axios.create({
baseURL: 'http://example.com/',
timeout: 30000, // 请求超时时间
withCredentials: true
});
debounce 配置项,防抖
/* request拦截器 */
service.interceptors.request.use(
(config) => {
if (config.url && /^https?:\/\//.test(config.url)) {
config.baseURL = '';
}
// debounce 配置项,防抖
if (config.debounce) {
// 生成cancelToken
config.cancelToken = new CancelToken((handleCancel) => {
removePending(config, handleCancel);
});
}
config.headers = !(() => {
let custom_headers = {
credentials: 'include'
};
if (config.data instanceof FormData) {
custom_headers['Content-Type'] = 'multipart/form-data';
}
return {
...custom_headers,
...config.headers
};
});
// 在这里可以做一些埋点的额外工作
return config;
},
(error: any) => {
Promise.reject(error);
}
);
/* respone拦截器 */
service.interceptors.response.use(
(response) => {
// 移除队列中的该请求
removePending(response.config);
const res = response.data;
if (response.status >= 400) {
throw new Error(
JSON.stringify({
code: response.status,
message: response.statusText
})
);
}
if (res.errcode || res.errno) {
// 错误处理
}
return Promise.resolve(res);
},
(error: any) => {
// 异常处理
if (error.message === '取消重复请求') {
return Promise.reject(error);
}
return Promise.reject(error);
}
);
/* 防止重复提交,利用axios的cancelToken */
let pending: string[] = []; // 声明一个数组用于存储每个ajax请求的取消函数和ajax标识
const CancelToken = axios.CancelToken;
const removePending = (config, handleCancel) => {
// 获取请求的url
const flagUrl = config.url;
// 判断该请求是否在请求队列中
if (flagUrl) {
if (pending.indexOf(flagUrl) !== -1) {
// 如果在请求中,并存在f,f即axios提供的取消函数
if (handleCancel) {
handleCancel('取消重复请求'); // 执行取消操作
} else {
pending.splice(pending.indexOf(flagUrl), 1); // 把这条记录从数组中移除
}
} else {
// 如果不存在在请求队列中,加入队列
if (handleCancel) {
pending.push(flagUrl);
}
}
}
};
使用泛型对返回数据类型进行了修饰
export function get<T>(
url: string,
params?: any,
options?: IHttpOption
): AxiosPromise<T> {
return service({
...{ options },
url,
method: 'GET',
params
});
}
使用
export const getList = (ids: Array<number>) => {
return get<{
list: ListType[];
}>('/api/url/', { ids });
};
基于 umiRequest 封装
对adaptor、timeout和headers进行了处理
import _ from 'lodash';
import { RequestConfig } from 'umi';
export const request: RequestConfig = {
timeout: 30000,
// 请求拦截
requestInterceptors: [
(url, options) => {
// 不包含 get 请求
if (
_.includes(['post', 'put'], _.lowerCase(options.method)) &&
options.requestType !== 'form' &&
!_.get(options, 'headers.Content-Type')
) {
// 向后端提交,添加 application/json
_.set(options, 'headers.Content-Type', 'application/json');
}
return { url, options };
},
],
errorConfig: {
adaptor: (res) => {
return {
...res,
success: res?.error === 0 || res?.code === 200,
errorMessage: res?.errorMessage || res?.msg || '接口出错',
};
},
},
};
request封装
export const request = (
url: string,
options: iKey<any>,
otherUrlHeader?: string,
) => umiRequest(`${otherUrlHeader || PrefixApiUri}${url}`, options);
使用
export const ajax1 = (data: DataType) => {
return request(`${BaseUrl}/api/url`, {
method: 'GET',
params: data,
});
};
export const ajax2 = (id: string) => {
return request(`${BaseUrl}/api/url/api`, {
method: 'POST',
data: {
get_id: `${id}`,
},
});
};
对于传递的数据,需要做一些额外的处理。
import _ from 'lodash';
import { RequestConfig } from 'umi';
import moment from "moment";
import { message } from "antd";
export const request: RequestConfig = {
timeout: 30000,
requestInterceptors: [
(url, options) => {
// 未设置content-type情况下,重置post
if (
_.includes(["post", "put"], _.lowerCase(options.method)) &&
options.requestType !== "form" &&
!_.get(options, "headers.Content-Type")
) {
_.set(options, "headers.Content-Type", "application/json");
}
return { url, options };
},
],
};
提交数据一般都是使用 post 方法。
// 封装 post 方法
export const post = (url: string, data?: any) => {
if (isObject(data) || Array.isArray(data)) {
return request(url, {
method: "post",
data: formatValues(data),
}).then((responseData) => {
const { error, errMessage } = responseData || {};
if (error !== 0) {
message.error(errMessage);
}
return responseData;
});
} else {
return request(url, { method: "post" }).then((responseData) => {
const { error, errMessage } = responseData || {};
if (error !== 0) {
message.error(errMessage);
}
return responseData;
});
}
};
formatValues
const isObject = (data: any): boolean => Object.prototype.toString.call(data) === "[object Object]";
const isString = (data: any): boolean => Object.prototype.toString.call(data) === "[object String]";
const isFalsyArray = (data: any[]) => {
if (Array.isArray(data)) {
return data.every((item) => item === null || item === "" || item === undefined);
}
return false;
};
export const formatValues = (data: any) => {
const newData = _.cloneDeep(data);
const formatDateTime = (data: any) => {
for (const [key, value] of Object.entries(data)) {
if (isString(value)) {
data[key] = (value as string).trim();
} else if (value instanceof moment) {
data[key] = moment(value).format('YYYY-MM-DD HH:mm:ss');
} else {
if (Array.isArray(value) || isObject(value)) {
formatDateTime(value);
}
}
//trim之后为空字符串的话就不要传给后端了
if (data[key] === "" || data[key] === null || isFalsyArray(data[key])) {
data[key] = undefined;
}
}
};
// 格式化时间数据,并且忽略''值和null
formatDateTime(newData);
return newData;
};
使用
post("/api/url", { id: 1 }).then((res: { data: DataType }) => { });