refactor(projects): 请求函数重构初步

This commit is contained in:
Soybean
2021-11-22 00:14:12 +08:00
parent e44f5d72a2
commit 9f64321d73
15 changed files with 185 additions and 165 deletions

View File

@ -3,20 +3,22 @@ import { ref } from 'vue';
export default function useBoolean(initValue: boolean = false) {
const bool = ref(initValue);
function setBool(value: boolean) {
bool.value = value;
}
function setTrue() {
bool.value = true;
setBool(true);
}
function setFalse() {
bool.value = false;
setBool(false);
}
function toggle() {
bool.value = !bool.value;
setBool(!bool.value);
}
return {
bool,
setBool,
setTrue,
setFalse,
toggle

View File

@ -0,0 +1 @@
export {};

1
src/service/api/index.ts Normal file
View File

@ -0,0 +1 @@
export * from './auth';

View File

@ -0,0 +1 @@
export * from './api';

View File

@ -0,0 +1 @@
export function handleResponse() {}

View File

@ -0,0 +1 @@
export * from './auth';

View File

@ -0,0 +1,8 @@
import type { AxiosRequestConfig } from 'axios';
import CustomAxiosInstance from './instance';
import Request from './request';
export function createRequest(axiosConfig: AxiosRequestConfig) {
const customInstance = new CustomAxiosInstance(axiosConfig);
return new Request(customInstance.instance);
}

View File

@ -1,9 +1,7 @@
import axios from 'axios';
import qs from 'qs';
import type { AxiosRequestConfig, AxiosInstance } from 'axios';
import { ContentType } from '@/enum';
import type { AxiosRequestConfig, AxiosInstance, CancelTokenStatic } from 'axios';
import { getToken } from '@/utils';
import { transformFile, errorHandler } from '../utils';
import { transformRequestData, handleResponseError } from '../helpers';
export interface StatusConfig {
/** 表明请求状态的属性key */
@ -16,54 +14,47 @@ export interface StatusConfig {
/**
* axios请求类
* @author Soybean() 2021-03-13
* @class CustomAxiosInstance
* @author Soybean<honghuangdc@gmail.com> 2021-03-13
*/
export default class CustomAxiosInstance {
instance: AxiosInstance;
constructor(
axiosConfig: AxiosRequestConfig,
statusConfig: StatusConfig = {
cancelToken: CancelTokenStatic;
private statusConfig: StatusConfig = {
statusKey: 'code',
msgKey: 'message',
successCode: 200
}
) {
};
constructor(axiosConfig: AxiosRequestConfig) {
this.instance = axios.create(axiosConfig);
this.setInterceptor(statusConfig);
this.cancelToken = axios.CancelToken;
this.setInterceptor();
}
/** 设置请求拦截器 */
setInterceptor(statusConfig: StatusConfig): void {
setInterceptor(): void {
this.instance.interceptors.request.use(
async config => {
const handleConfig = { ...config };
if (handleConfig.headers) {
// form类型转换
if (handleConfig.headers['Content-Type'] === ContentType.formUrlencoded) {
handleConfig.data = qs.stringify(handleConfig.data);
}
// 文件类型转换
if (handleConfig?.headers['Content-Type'] === ContentType.formData) {
const key = Object.keys(handleConfig.data)[0];
const file = handleConfig.data[key];
handleConfig.data = await transformFile(file, key);
}
// 数据转换
handleConfig.data = await transformRequestData(handleConfig.data, handleConfig.headers['Content-Type']);
// 设置token
handleConfig.headers.Authorization = getToken();
}
return handleConfig;
},
error => {
errorHandler(error);
handleResponseError(error);
return Promise.reject(error);
}
);
this.instance.interceptors.response.use(
response => {
const { status, data } = response;
const { statusKey, msgKey, successCode } = statusConfig;
const { statusKey, msgKey, successCode } = this.statusConfig;
if (status === 200 || status < 300 || status === 304) {
const responseData = data as any;
if (responseData[statusKey] === successCode) {
@ -73,11 +64,11 @@ export default class CustomAxiosInstance {
return Promise.reject(responseData[msgKey]);
}
const error = { response };
errorHandler(error);
handleResponseError(error);
return Promise.reject(error);
},
error => {
errorHandler(error);
handleResponseError(error);
return Promise.reject(error);
}
);

View File

@ -1,6 +1,4 @@
import type { AxiosRequestConfig, AxiosInstance, AxiosResponse } from 'axios';
import CustomAxiosInstance from './instance';
import type { StatusConfig } from './instance';
type ResponseSuccess = [null, any];
type ResponseFail = [any, null];
@ -10,7 +8,7 @@ type ResponseFail = [any, null];
* @author Soybean<honghuangdc@gmail.com> 2021-03-15
* @class Request
*/
class Request {
export default class Request {
instance: AxiosInstance;
constructor(instance: AxiosInstance) {
@ -44,8 +42,37 @@ class Request {
}
}
export function createRequest(axiosConfig: AxiosRequestConfig, statusConfig?: StatusConfig) {
const customInstance = new CustomAxiosInstance(axiosConfig, statusConfig);
const request = new Request(customInstance.instance);
return request;
}
// import type { AxiosRequestConfig, AxiosInstance } from 'axios';
// import { useBoolean } from '@/hooks';
// type RequestMethod = 'get' | 'post' | 'put' | 'delete';
// interface RequestParam<ResponseData> {
// /** axios实例 */
// instance: AxiosInstance;
// /** 请求地址 */
// url: string;
// /** 请求方法 */
// method?: RequestMethod;
// /** axios请求配置 */
// axiosConfig?: AxiosRequestConfig;
// /** 请求结果的数据判断是否为空的函数 */
// responseDataEmptyFunc?: (data: ResponseData) => boolean;
// /** 全局请求错误时是否弹出消息 */
// showErrorMsg?: boolean;
// }
// /**
// * 请求函数hooks
// * @param requestParam - 请求函数的参数
// * @param url - 请求地址
// * @param axiosConfig
// */
// export default function useRequest<ResponseData>(requestParam: RequestParam<ResponseData>) {
// /** 网络状况 */
// const { bool: networkStatus, setBool: setNetworkStatus } = useBoolean(window.navigator.onLine);
// /** 是否正在请求 */
// const { bool: isFetching, setBool: setIsFetching } = useBoolean();
// /** 响应的结果数据是否为空 */
// const { bool: isEmpty, setBool: setIsEmpty } = useBoolean();
// }

View File

@ -0,0 +1,50 @@
const ERROR_STATUS = {
400: '400: 请求出现语法错误',
401: '401: 用户未授权~',
403: '403: 服务器拒绝访问~',
404: '404: 请求的资源不存在~',
405: '405: 请求方法未允许~',
408: '408: 网络请求超时~',
500: '500: 服务器内部错误~',
501: '501: 服务器未实现请求功能~',
502: '502: 错误网关~',
503: '503: 服务不可用~',
504: '504: 网关超时~',
505: '505: http版本不支持该请求~'
};
type ErrorStatus = keyof typeof ERROR_STATUS;
type ErrorMsg = [boolean, string];
/**
* 获取请求失败的错误
* @param error
*/
export function getFailRequestErrorMsg(error: any) {
const errorAction: ErrorMsg[] = [
[!window.navigator.onLine || error.message === 'Network Error', '网络不可用~'],
[error.code === 'ECONNABORTED' && error.message.includes('timeout'), '网络连接超时~'],
[error.response, ERROR_STATUS[error.response.status as ErrorStatus]]
];
let errorMsg = '请求错误~';
errorAction.some(item => {
const [flag, msg] = item;
if (flag) {
errorMsg = msg;
}
return flag;
});
return errorMsg;
}
/**
* 处理请求失败的错误
* @param error - 错误
*/
export function handleResponseError(error: any) {
const { $message: Message } = window;
const msg = getFailRequestErrorMsg(error);
Message?.error(msg);
}

View File

@ -0,0 +1,16 @@
// type ResultHandler<T> = (...arg: any) => T;
// type SuccessRequest<T> = {
// error: null;
// data: T;
// };
// type FailRequest = {
// error: any;
// data: null;
// };
// type RequestResult<T> = SuccessRequest<T> | FailRequest;
// /**
// * 对请求的结果数据进行格式化的处理
// * @param handleFunc - 处理函数
// * @param requests - 请求结果
// */
// export function handleResponse<ResponseData>(resultHandler: ResultHandler<ResponseData>, requests: RequestResult[]) {}

View File

@ -0,0 +1,2 @@
export * from './transform';
export * from './error';

View File

@ -0,0 +1,40 @@
import qs from 'qs';
import FormData from 'form-data';
import { isArray } from '@/utils';
import { ContentType } from '@/enum';
export async function transformRequestData(requestData: any, contentType?: string) {
// application/json类型不处理
let data = requestData;
// form类型转换
if (contentType === ContentType.formUrlencoded) {
data = qs.stringify(requestData);
}
// form-data类型转换
if (contentType === ContentType.formData) {
const key = Object.keys(requestData)[0];
const file = requestData.data[key];
data = await transformFile(file, key);
}
return data;
}
/**
* 接口为上传文件的类型时数据转换
* @param file - 单文件或多文件
* @param key - 文件的属性名
*/
async function transformFile(file: File[] | File, key: string) {
const formData = new FormData();
if (isArray(file)) {
await Promise.all(
(file as File[]).map(item => {
formData.append(key, item);
return true;
})
);
} else {
await formData.append(key, file);
}
return formData;
}

View File

@ -1,4 +1,4 @@
import { createRequest } from './request';
import { createRequest } from './axios';
export const adminRequest = createRequest({
baseURL: import.meta.env.VITE_HTTP_URL_EMOSS_ADMIN as string

View File

@ -1,121 +0,0 @@
import FormData from 'form-data';
import { isArray } from '@/utils';
type HandleFunc<T> = (...arg: any) => T;
type RequestError = any;
type RequestData = any;
type RequestResult = [RequestError, RequestData];
/**
* 对请求的结果数据进行格式化的处理
* @param handleFunc - 处理函数
* @param requests - 请求结果
*/
export function handleResponse<T>(handleFunc: HandleFunc<T>, ...requests: RequestResult[]) {
let handleData: any = null;
let error: any = null;
const hasError = requests.some(item => {
const isError = Boolean(item[0]);
if (isError) {
[error] = item;
}
return isError;
});
if (!hasError) {
handleData = handleFunc(...requests.map(item => item[1]));
}
return [error, handleData] as [any, T];
}
/**
* 接口为上传文件的类型时数据转换
* @param file - 单文件或多文件
* @param key - 文件的属性名
*/
export async function transformFile(file: File[] | File, key: string) {
const formData = new FormData();
if (isArray(file)) {
await Promise.all(
(file as File[]).map(item => {
formData.append(key, item);
return true;
})
);
} else {
await formData.append(key, file);
}
return formData;
}
const ERROR_STATUS = {
400: '400: 请求出现语法错误',
401: '401: 用户未授权~',
403: '403: 服务器拒绝访问~',
404: '404: 请求的资源不存在~',
405: '405: 请求方法未允许~',
408: '408: 网络请求超时~',
500: '500: 服务器内部错误~',
501: '501: 服务器未实现请求功能~',
502: '502: 错误网关~',
503: '503: 服务不可用~',
504: '504: 网关超时~',
505: '505: http版本不支持该请求~'
};
type ErrorStatus = keyof typeof ERROR_STATUS;
/**
* 网络请求错误状态处理
* @param error - 错误
*/
export function errorHandler(error: any): void {
const { $message: Message } = window;
if (error.response) {
const status = error.response.status as ErrorStatus;
Message?.error(ERROR_STATUS[status]);
return;
}
if (error.code === 'ECONNABORTED' && error.message.includes('timeout')) {
Message?.error('网络连接超时~');
return;
}
if (!window.navigator.onLine || error.message === 'Network Error') {
Message?.error('网络不可用~');
return;
}
Message?.error('请求错误~');
}
/**
* 连续的请求错误依此显示
* @param duration - 上一次弹出错误消息到下一次的时间(ms)
*/
export function continuousErrorHandler(duration: number) {
let errorStacks: string[] = [];
function pushError(id: string) {
errorStacks.push(id);
}
function removeError(id: string) {
errorStacks = errorStacks.filter(item => item !== id);
}
function handleError(id: string, callback: Function) {
callback();
setTimeout(() => {
removeError(id);
}, duration);
}
function handleContinuousError(callback: Function) {
const id = Date.now().toString(36);
const { length } = errorStacks;
if (length > 0) {
pushError(id);
setTimeout(() => {
handleError(id, callback);
}, duration * length);
} else {
pushError(id);
handleError(id, callback);
}
}
return handleContinuousError;
}