refactor(projects): axios封装完成

This commit is contained in:
Soybean 2021-11-23 00:23:43 +08:00
parent 451c7547af
commit 03b398af2f
21 changed files with 332 additions and 145 deletions

View File

@ -1,4 +1,4 @@
#请求的环境
VITE_HTTP_ENV=DEV
#请求地址
VITE_HTTP_URL=http://192.168.100.43:8201
VITE_HTTP_URL=https://test.aisuit.com.cn

View File

@ -0,0 +1,15 @@
/** 数据字典 */
export interface Dictionary {
/** 字典名字 */
label: string;
/** 要素名字(一级指标) */
charactorLabel: string;
/** 要素下的指标key(二级指标) */
indicatorKey: string;
/** 要素下的指标名字(二级指标) */
indicatorLabel: string;
/** 备注 */
remark: string;
/** 指标公式 */
formula: string;
}

View File

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

View File

@ -0,0 +1,15 @@
/** 数据字典 */
export interface ResponseDictionary {
/** 字典名字 */
modelName: string;
/** 要素名字(一级指标) */
modelCharactorName: string;
/** 要素下的指标key(二级指标) */
modelIndicator: string;
/** 要素下的指标名字(二级指标) */
modelIndicatorName: string;
/** 备注 */
remarks: string;
/** 指标公式 */
formula: string;
}

View File

@ -1,2 +1,4 @@
export * from './system';
export * from './route';
export * from './service';
export * from './api';

View File

@ -0,0 +1,50 @@
/**
*
* - axios: axios错误, ,
* - http: 请求成功200
* - backend: 请求成功200
*/
export type RequestServiceErrorType = 'axios' | 'http' | 'backend';
/** 请求服务的错误 */
export interface RequestServiceError {
/** 请求服务的错误类型 */
type: RequestServiceErrorType;
/** 错误码 */
code: string | number;
/** 错误信息 */
msg: string;
}
/** 后端接口返回的类型结构 */
export interface BackendServiceResult {
/** 状态码 */
code: number;
/** 接口数据 */
data: any;
/** 接口消息 */
message: string;
}
/** 自定义的请求成功结果 */
export interface CustomSuccessRequestResult<ResponseData> {
/** 请求错误 */
error: null;
/** 请求数据 */
data: ResponseData;
/** 网络状态 */
networkStatus: boolean;
}
/** 自定义的请求失败结果 */
export interface CustomFailRequestResult {
/** 请求错误 */
error: RequestServiceError;
/** 请求数据 */
data: null;
/** 网络状态 */
networkStatus: boolean;
}
/** 自定义的请求结果 */
export type CustomRequestResult<ResponseData> = CustomSuccessRequestResult<ResponseData> | CustomFailRequestResult;

View File

@ -1,8 +0,0 @@
import { request } from '../request';
/**
*
*/
export async function fetchDictionary(keyword: string) {
await request.post('/ehe/model/getByIndicator', { indiCatorName: keyword });
}

27
src/service/api/demo.ts Normal file
View File

@ -0,0 +1,27 @@
import type { ResponseDictionary, Dictionary } from '@/interface';
import { request, resultMiddleware } from '../request';
import { fecthDictionaryMiddleware } from '../middleware';
// 接口示例
/**
* (middleware处理)
* @param keyword -
*/
export function fetchDictionary(keyword: string) {
return request.post<ResponseDictionary[]>('/emoss-entropy/ehe/model/getByIndicator', {
indiCatorName: keyword
});
}
/**
* (middleware处理)
* @param keyword -
*/
export async function fetchDictionaryWithMiddleware(keyword: string) {
const res = await request.post<ResponseDictionary[]>('/emoss-entropy/ehe/model/getByIndicator', {
indiCatorName: keyword
});
return resultMiddleware<Dictionary[]>(fecthDictionaryMiddleware, [res]);
}

View File

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

View File

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

View File

@ -0,0 +1,23 @@
import type { ResponseDictionary, Dictionary } from '@/interface';
export function fecthDictionaryMiddleware(data: ResponseDictionary[]): Dictionary[] {
return data.map(item => {
const {
modelName: label,
modelCharactorName: charactorLabel,
modelIndicator: indicatorKey,
modelIndicatorName: indicatorLabel,
remarks: remark,
formula
} = item;
return {
label,
charactorLabel,
indicatorKey,
indicatorLabel,
remark,
formula
};
});
}

View File

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

View File

@ -1,31 +1,19 @@
import axios from 'axios';
import type { AxiosRequestConfig, AxiosInstance, AxiosError, CancelTokenStatic } from 'axios';
import { getToken } from '@/utils';
import { transformRequestData, handleAxiosError, handleResponseError } from '../helpers';
interface StatusConfig {
/** 表明请求状态的属性key */
statusKey: string;
/** 请求信息的属性key */
msgKey: string;
/** 成功状态的状态码 */
successCode: string | number;
}
import type { BackendServiceResult } from '@/interface';
import { transformRequestData, handleAxiosError, handleResponseError, handleBackendError } from '../helpers';
/**
* axios请求类
* @author Soybean<honghuangdc@gmail.com> 2021-03-13
* @author Soybean<honghuangdc@gmail.com>
*/
export default class CustomAxiosInstance {
instance: AxiosInstance;
cancelToken: CancelTokenStatic;
private backendSuccessCode = 200;
private statusConfig: StatusConfig = {
statusKey: 'code',
msgKey: 'message',
successCode: 200
};
cancelToken: CancelTokenStatic;
constructor(axiosConfig: AxiosRequestConfig) {
this.instance = axios.create(axiosConfig);
@ -40,35 +28,34 @@ export default class CustomAxiosInstance {
const handleConfig = { ...config };
if (handleConfig.headers) {
// 数据转换
handleConfig.data = await transformRequestData(handleConfig.data, handleConfig.headers['Content-Type']);
const contentType = handleConfig.headers['Content-Type'];
handleConfig.data = await transformRequestData(handleConfig.data, contentType);
// 设置token
handleConfig.headers.Authorization = getToken();
}
return handleConfig;
},
(error: AxiosError) => {
handleAxiosError(error);
(axiosError: AxiosError) => {
const error = handleAxiosError(axiosError);
return Promise.reject(error);
}
);
this.instance.interceptors.response.use(
response => {
const { status, data } = response;
const { statusKey, msgKey, successCode } = this.statusConfig;
const { status } = response;
if (status === 200 || status < 300 || status === 304) {
const responseData = data as any;
if (responseData[statusKey] === successCode) {
return Promise.resolve(responseData.data);
const backendServiceResult = response.data as BackendServiceResult;
if (backendServiceResult.code === this.backendSuccessCode) {
return Promise.resolve(backendServiceResult.data);
}
window.$message?.error(responseData[msgKey]);
return Promise.reject(responseData[msgKey]);
const error = handleBackendError(backendServiceResult);
return Promise.reject(error);
}
const error = { response };
handleResponseError(response);
const error = handleResponseError(response);
return Promise.reject(error);
},
(error: AxiosError) => {
handleAxiosError(error);
(axiosError: AxiosError) => {
const error = handleAxiosError(axiosError);
return Promise.reject(error);
}
);

View File

@ -1,12 +1,9 @@
import type { AxiosRequestConfig, AxiosInstance, AxiosResponse } from 'axios';
type ResponseSuccess = [null, any];
type ResponseFail = [any, null];
import type { RequestServiceError, CustomSuccessRequestResult, CustomFailRequestResult } from '@/interface';
/**
*
* @author Soybean<honghuangdc@gmail.com> 2021-03-15
* @class Request
* @author Soybean<honghuangdc@gmail.com>
*/
export default class Request {
instance: AxiosInstance;
@ -15,64 +12,51 @@ export default class Request {
this.instance = instance;
}
static successHandler(response: AxiosResponse) {
const result: ResponseSuccess = [null, response];
return result;
static successHandler<ResponseData>(response: AxiosResponse) {
const successResult: CustomSuccessRequestResult<ResponseData> = {
data: response as unknown as ResponseData,
error: null,
networkStatus: window.navigator.onLine
};
return successResult;
}
static failHandler(error: any) {
const result: ResponseFail = [error, null];
return result;
static failHandler(error: RequestServiceError) {
const failResult: CustomFailRequestResult = {
data: null,
error,
networkStatus: window.navigator.onLine
};
return failResult;
}
get(url: string, config?: AxiosRequestConfig) {
return this.instance.get(url, config).then(Request.successHandler).catch(Request.failHandler);
get<ResponseData>(url: string, config?: AxiosRequestConfig) {
return this.instance
.get(url, config)
.then(res => Request.successHandler<ResponseData>(res))
.catch(Request.failHandler);
}
post(url: string, data?: any, config?: AxiosRequestConfig) {
return this.instance.post(url, data, config).then(Request.successHandler).catch(Request.failHandler);
post<ResponseData>(url: string, data?: any, config?: AxiosRequestConfig) {
return this.instance
.post(url, data, config)
.then(res => Request.successHandler<ResponseData>(res))
.catch(Request.failHandler);
}
put(url: string, data?: any, config?: AxiosRequestConfig) {
return this.instance.put(url, data, config).then(Request.successHandler).catch(Request.failHandler);
put<ResponseData>(url: string, data?: any, config?: AxiosRequestConfig) {
return this.instance
.put(url, data, config)
.then(res => Request.successHandler<ResponseData>(res))
.catch(Request.failHandler);
}
delete(url: string, config?: AxiosRequestConfig) {
return this.instance.delete(url, config).then(Request.successHandler).catch(Request.failHandler);
delete<ResponseData>(url: string, config?: AxiosRequestConfig) {
return this.instance
.delete(url, config)
.then(res => Request.successHandler<ResponseData>(res))
.catch(Request.failHandler);
}
}
// 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

@ -1,12 +1,21 @@
/** 请求超时时间 */
export const REQUEST_TIMEOUT = 60 * 1000;
/** 默认的请求错误文本 */
/** 错误信息的显示时间 */
export const ERROR_MSG_DURATION = 3 * 1000;
/** 兜底的请求错误code */
export const DEFAULT_REQUEST_ERROR_CODE = 'DEFAULT';
/** 兜底的请求错误文本 */
export const DEFAULT_REQUEST_ERROR_MSG = '请求错误~';
/** 请求超时的错误code(为固定值ECONNABORTED) */
export const REQUEST_TIMEOUT_CODE = 'ECONNABORTED';
/** 请求超时的错误文本 */
export const REQUEST_TIMEOUT_MSG = '请求超时~';
/** 网络不可用的code */
export const NETWORK_ERROR_CODE = 'NETWORK_ERROR';
/** 网络不可用的错误文本 */
export const NETWORK_ERROR_MSG = '网络不可用~';
@ -25,3 +34,6 @@ export const ERROR_STATUS = {
504: '504: 网关超时~',
505: '505: http版本不支持该请求~'
};
/** 不弹出错误信息的code */
export const NO_ERROR_MSG_CODE: (string | number)[] = [];

View File

@ -1,37 +1,45 @@
import type { AxiosError, AxiosResponse } from 'axios';
import { DEFAULT_REQUEST_ERROR_MSG, ERROR_STATUS, NETWORK_ERROR_MSG } from '../config';
import type { RequestServiceError, BackendServiceResult } from '@/interface';
import {
DEFAULT_REQUEST_ERROR_CODE,
DEFAULT_REQUEST_ERROR_MSG,
NETWORK_ERROR_CODE,
NETWORK_ERROR_MSG,
REQUEST_TIMEOUT_CODE,
REQUEST_TIMEOUT_MSG,
ERROR_STATUS
} from '../config';
import { showErrorMsg } from './msg';
type ErrorStatus = keyof typeof ERROR_STATUS;
/**
*
* @param error
*/
export function getFailRequestErrorMsg(error: AxiosError) {
if (!window.navigator.onLine || error.message === 'Network Error') {
return NETWORK_ERROR_MSG;
}
if (error.code === 'ECONNABORTED') {
return error.message;
}
if (error.response) {
const errorCode: ErrorStatus = error.response.status as ErrorStatus;
const msg = ERROR_STATUS[errorCode] || DEFAULT_REQUEST_ERROR_MSG;
return msg;
}
return DEFAULT_REQUEST_ERROR_MSG;
}
/**
*
* @param error -
*/
export function handleAxiosError(error: AxiosError) {
const { $message: Message } = window;
export function handleAxiosError(axiosError: AxiosError) {
const error: RequestServiceError = {
type: 'axios',
code: DEFAULT_REQUEST_ERROR_CODE,
msg: DEFAULT_REQUEST_ERROR_MSG
};
const msg = getFailRequestErrorMsg(error);
if (!window.navigator.onLine || axiosError.message === 'Network Error') {
// 网路错误
Object.assign(error, { code: NETWORK_ERROR_CODE, msg: NETWORK_ERROR_MSG });
} else if (axiosError.code === REQUEST_TIMEOUT_CODE && axiosError.message.includes('timeout')) {
/** 超时错误 */
Object.assign(error, { code: REQUEST_TIMEOUT_CODE, msg: REQUEST_TIMEOUT_MSG });
} else if (axiosError.response) {
// 请求不成功的错误
const errorCode: ErrorStatus = axiosError.response.status as ErrorStatus;
const msg = ERROR_STATUS[errorCode] || DEFAULT_REQUEST_ERROR_MSG;
Object.assign(error, { code: errorCode, msg });
}
Message?.error(msg);
showErrorMsg(error);
return error;
}
/**
@ -39,10 +47,39 @@ export function handleAxiosError(error: AxiosError) {
* @param response -
*/
export function handleResponseError(response: AxiosResponse) {
const error: RequestServiceError = {
type: 'axios',
code: DEFAULT_REQUEST_ERROR_CODE,
msg: DEFAULT_REQUEST_ERROR_MSG
};
if (!window.navigator.onLine) {
return NETWORK_ERROR_MSG;
// 网路错误
Object.assign(error, { code: NETWORK_ERROR_CODE, msg: NETWORK_ERROR_MSG });
} else {
// 请求成功的状态码非200的错误
const errorCode: ErrorStatus = response.status as ErrorStatus;
const msg = ERROR_STATUS[errorCode] || DEFAULT_REQUEST_ERROR_MSG;
Object.assign(error, { type: 'backend', code: errorCode, msg });
}
const errorCode: ErrorStatus = response.status as ErrorStatus;
const msg = ERROR_STATUS[errorCode] || DEFAULT_REQUEST_ERROR_MSG;
return msg;
showErrorMsg(error);
return error;
}
/**
*
* @param backendResult -
*/
export function handleBackendError(backendResult: BackendServiceResult) {
const error: RequestServiceError = {
type: 'backend',
code: backendResult.code,
msg: backendResult.message
};
showErrorMsg(error);
return error;
}

View File

@ -1,16 +1,26 @@
// 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[]) {}
import { CustomRequestResult, CustomSuccessRequestResult, CustomFailRequestResult } from '@/interface';
type ResultHandler<T> = (...arg: any) => T;
/**
*
* @param resultHandler -
* @param requests -
*/
export function resultMiddleware<MiddlewareData>(
resultHandler: ResultHandler<MiddlewareData>,
requests: CustomRequestResult<any>[]
) {
const errorIndex = requests.findIndex(item => item.error !== null);
const hasError = errorIndex > -1;
const successResult: CustomSuccessRequestResult<MiddlewareData> = {
data: resultHandler(...requests.map(item => item.data)),
error: null,
networkStatus: window.navigator.onLine
};
const failResult: CustomFailRequestResult = {
data: null,
error: requests[errorIndex].error!,
networkStatus: window.navigator.onLine
};
return hasError ? failResult : successResult;
}

View File

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

View File

@ -0,0 +1,31 @@
import type { RequestServiceError } from '@/interface';
import { NO_ERROR_MSG_CODE, ERROR_MSG_DURATION } from '../config';
/** 错误消息栈,防止同一错误同时出现 */
const errorMsgStack = new Map<string | number, string>([]);
function addErrorMsg(error: RequestServiceError) {
errorMsgStack.set(error.code, error.msg);
}
function removeErrorMsg(error: RequestServiceError) {
errorMsgStack.delete(error.code);
}
function hasErrorMsg(error: RequestServiceError) {
return errorMsgStack.has(error.code);
}
/**
*
* @param error
*/
export function showErrorMsg(error: RequestServiceError) {
if (!NO_ERROR_MSG_CODE.includes(error.code)) {
if (!hasErrorMsg(error)) {
addErrorMsg(error);
window.$message?.error(error.msg, { duration: ERROR_MSG_DURATION });
setTimeout(() => {
removeErrorMsg(error);
}, ERROR_MSG_DURATION);
}
}
}

View File

@ -1,8 +1,9 @@
import { createRequest } from './axios';
import { REQUEST_TIMEOUT, REQUEST_TIMEOUT_MSG } from './config';
import { REQUEST_TIMEOUT } from './config';
export const request = createRequest({
baseURL: import.meta.env.VITE_HTTP_URL,
timeout: REQUEST_TIMEOUT,
timeoutErrorMessage: REQUEST_TIMEOUT_MSG
timeout: REQUEST_TIMEOUT
});
export { resultMiddleware } from './helpers';