| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import { type IRequestOptions, BoleRequest, type RequestConfig } from '@bole-core/core'; |
| | | import { type AxiosRequestConfig, type AxiosError } from 'axios'; |
| | | import qs from 'qs'; |
| | | import NProgress from '../progress'; |
| | | import { loadEnv } from '@build/index'; |
| | | import { getToken, getUserInfo } from '../storage'; |
| | | import { useUserStoreHook } from '@/store/modules/user'; |
| | | import { ElLoading as Loading, ElNotification } from 'element-plus'; |
| | | import { router } from '@/router'; |
| | | import { Message, tokenIsExpired } from '@/utils'; |
| | | |
| | | // å è½½ç¯å¢åé VITE_PROXY_DOMAINï¼å¼åç¯å¢ï¼ VITE_PROXY_DOMAIN_REALï¼æå
åç线ä¸ç¯å¢ï¼ |
| | | const { VITE_PROXY_DOMAIN, VITE_PROXY_DOMAIN_REAL, DEV } = loadEnv(); |
| | | |
| | | const RefreshTokenUrl = '/GetTokenByRefreshToken'; |
| | | |
| | | /** 请æ±ç½ååï¼æ¾ç½®ä¸äºä¸éè¦tokençæ¥å£ï¼éè¿è®¾ç½®è¯·æ±ç½ååï¼é²æ¢tokenè¿æåå请æ±é æçæ»å¾ªç¯é®é¢ï¼ */ |
| | | const whiteList = [RefreshTokenUrl, '/GetToken']; |
| | | |
| | | let loadingInstance: ReturnType<typeof Loading.service>; |
| | | |
| | | function startLoading() { |
| | | loadingInstance = Loading.service({ |
| | | fullscreen: true, |
| | | lock: true, |
| | | background: 'transparent', |
| | | }); |
| | | } |
| | | |
| | | function endLoading() { |
| | | if (loadingInstance) { |
| | | loadingInstance.close(); |
| | | } |
| | | } |
| | | |
| | | // é误å¤çæ¹æ¡ï¼ é误类å |
| | | enum ErrorShowType { |
| | | /** |
| | | * å¥ä¹ä¸å¹² |
| | | */ |
| | | SILENT = 0, |
| | | |
| | | WARN_MESSAGE = 1, |
| | | ERROR_MESSAGE = 2, |
| | | /** |
| | | * éç¥æ¥é |
| | | */ |
| | | NOTIFICATION = 3, |
| | | /** |
| | | * éå®å |
| | | */ |
| | | REDIRECT = 9, |
| | | } |
| | | // ä¸å端约å®çååºæ°æ®æ ¼å¼ |
| | | interface ResponseStructure { |
| | | success: boolean; |
| | | data: any; |
| | | /** |
| | | * é误ç |
| | | */ |
| | | error?: number; |
| | | /** |
| | | * éè¯¯ä¿¡æ¯ |
| | | */ |
| | | msg?: string; |
| | | showType?: ErrorShowType; |
| | | } |
| | | |
| | | interface ErrorResponse { |
| | | error?: { |
| | | data: any; |
| | | code: number; |
| | | message: string; |
| | | }; |
| | | } |
| | | |
| | | interface ErrorInfo { |
| | | errorCode?: number; |
| | | errorMessage?: string; |
| | | showType?: ErrorShowType; |
| | | data: any; |
| | | } |
| | | |
| | | const AxiosOptions: AxiosRequestConfig = { |
| | | baseURL: DEV ? VITE_PROXY_DOMAIN : VITE_PROXY_DOMAIN_REAL, |
| | | timeout: 30000, |
| | | headers: { |
| | | Accept: 'application/json, text/plain, */*', |
| | | 'Content-Type': 'application/json', |
| | | 'X-Requested-With': 'XMLHttpRequest', |
| | | }, |
| | | // æ°ç»æ ¼å¼åæ°åºåå |
| | | paramsSerializer: { |
| | | //@ts-ignore |
| | | serialize: (params) => qs.stringify(params, { indices: false }), |
| | | }, |
| | | withCredentials: true, |
| | | }; |
| | | |
| | | const config: RequestConfig<ResponseStructure, IRequestOptions> = { |
| | | ...AxiosOptions, |
| | | |
| | | errorConfig: { |
| | | // éè¯¯æ¥æ¶åå¤ç |
| | | errorHandler: (error, opts) => { |
| | | console.log('error: ', error); |
| | | if (opts?.skipErrorHandler) throw error; |
| | | |
| | | if (opts?.customErrorHandler) { |
| | | if (opts?.customErrorHandler(error)) { |
| | | return; |
| | | } |
| | | } |
| | | |
| | | // æä»¬ç errorThrower æåºçé误ã |
| | | if (error.name === 'BizError') { |
| | | const errorInfo: ErrorInfo | undefined = (error as any).info; |
| | | if (errorInfo) { |
| | | const { errorMessage, errorCode } = errorInfo; |
| | | switch (errorInfo.showType) { |
| | | case ErrorShowType.SILENT: |
| | | // do nothing |
| | | break; |
| | | case ErrorShowType.WARN_MESSAGE: |
| | | Message.warnMessage(errorMessage); |
| | | break; |
| | | case ErrorShowType.ERROR_MESSAGE: |
| | | Message.errorMessage(errorMessage); |
| | | break; |
| | | case ErrorShowType.NOTIFICATION: |
| | | ElNotification.error({ |
| | | message: `${errorCode}: ${errorMessage}`, |
| | | }); |
| | | break; |
| | | case ErrorShowType.REDIRECT: |
| | | // TODO: redirect |
| | | break; |
| | | default: |
| | | Message.errorMessage(errorMessage); |
| | | } |
| | | } |
| | | } else if ((error as AxiosError<ResponseStructure, IRequestOptions>).response) { |
| | | // Axios çé误 |
| | | // è¯·æ±æåååºä¸æå¡å¨ä¹ååºäºç¶æç ï¼ä½ç¶æä»£ç è¶
åºäº 2xx çèå´ |
| | | handleAxiosResponseError(error as AxiosError<ErrorResponse, IRequestOptions>); |
| | | // Message.errorMessage(`Response status:${(error as AxiosError).response.status}`); |
| | | } else if ((error as AxiosError).request) { |
| | | // 请æ±å·²ç»æååèµ·ï¼ä½æ²¡ææ¶å°ååº |
| | | // \`error.request\` 卿µè§å¨ä¸æ¯ XMLHttpRequest çå®ä¾ï¼ |
| | | // èå¨node.js䏿¯ http.ClientRequest çå®ä¾ |
| | | Message.errorMessage('æå¡å¨æ ååºï¼è¯·éè¯'); |
| | | } else { |
| | | // åéè¯·æ±æ¶åºäºç¹é®é¢ |
| | | Message.errorMessage('åéè¯·æ±æ¶åºäºç¹é®é¢'); |
| | | } |
| | | }, |
| | | |
| | | // é误æåº |
| | | errorThrower: (res) => { |
| | | const { success, data, error: errorCode, msg, showType } = res; |
| | | if (!success) { |
| | | const error: any = new Error(msg); |
| | | error.name = 'BizError'; |
| | | error.info = { errorCode, errorMessage: msg, showType, data }; |
| | | throw error; // æåºèªå¶çé误 |
| | | } |
| | | }, |
| | | }, |
| | | |
| | | requestInterceptors: [ |
| | | [ |
| | | (config) => { |
| | | const $config = config; |
| | | // å¼å¯è¿åº¦æ¡å¨ç» |
| | | if (config.needNProcess) { |
| | | NProgress.start(); |
| | | } |
| | | |
| | | const token = getToken(); |
| | | |
| | | const userInfo = getUserInfo(); |
| | | |
| | | const { showLoading = true, mock } = $config; |
| | | |
| | | if (mock) { |
| | | $config.url = $config.url.replace(/^\/api/, ''); |
| | | } |
| | | |
| | | /** |
| | | * 妿æ¯refreshTokenè¿ä¸ªæ¹æ³ å°±ç´æ¥è¿å 鲿¢å
åæ³æ¼ |
| | | */ |
| | | if (whiteList.some((url) => $config.url.toLowerCase().includes(url.toLowerCase()))) { |
| | | return $config; |
| | | } |
| | | |
| | | if (showLoading) { |
| | | startLoading(); |
| | | } |
| | | |
| | | return new Promise((resolve) => { |
| | | if (token && $config.withCredentials) { |
| | | if (tokenIsExpired(userInfo)) { |
| | | if (!BoleRequest.refreshTokenPending) { |
| | | const userStore = useUserStoreHook(); |
| | | BoleRequest.refreshTokenPending = true; |
| | | // tokenè¿æå·æ° |
| | | userStore |
| | | .refreshToken({ |
| | | refreshToken: userInfo.refreshToken, |
| | | clientId: userStore.accountInfo.client_id, |
| | | }) |
| | | .then((res) => { |
| | | $config.headers['Authorization'] = 'Bearer ' + res.accessToken; |
| | | BoleRequest.requests.forEach((cb) => cb(res.accessToken)); |
| | | BoleRequest.requests = []; |
| | | }) |
| | | .finally(() => { |
| | | BoleRequest.refreshTokenPending = false; |
| | | }); |
| | | } |
| | | resolve(BoleRequest.retryOriginalRequest($config)); |
| | | } else { |
| | | $config.headers['Authorization'] = 'Bearer ' + token; |
| | | resolve($config); |
| | | } |
| | | } else { |
| | | resolve($config); |
| | | } |
| | | }); |
| | | }, |
| | | (error: AxiosError) => { |
| | | endLoading(); |
| | | return Promise.reject(error); |
| | | }, |
| | | ], |
| | | ], |
| | | responseInterceptors: [ |
| | | [ |
| | | (response) => { |
| | | const $config = response.config as IRequestOptions; |
| | | |
| | | const { needNProcess, getResponse = false } = $config; |
| | | |
| | | // å
³éè¿åº¦æ¡å¨ç» |
| | | if (needNProcess) { |
| | | NProgress.done(); |
| | | } |
| | | |
| | | endLoading(); |
| | | |
| | | return getResponse ? response : (response.data as any).result; |
| | | }, |
| | | (error) => { |
| | | endLoading(); |
| | | return Promise.reject(error); |
| | | }, |
| | | ], |
| | | ], |
| | | }; |
| | | |
| | | const ErrorMessageMap = { |
| | | [400]: '请æ±é误', |
| | | [401]: 'æªææï¼è¯·ç»å½', |
| | | [403]: 'æç»è®¿é®', |
| | | [404]: '请æ±å°ååºé', |
| | | [408]: '请æ±è¶
æ¶', |
| | | [500]: 'æå¡å¨å
é¨é误', |
| | | [501]: 'æå¡æªå®ç°', |
| | | [502]: 'ç½å
³é误', |
| | | [503]: 'æå¡ä¸å¯ç¨', |
| | | [504]: 'ç½å
³è¶
æ¶', |
| | | [505]: 'HTTPçæ¬ä¸åæ¯æ', |
| | | }; |
| | | |
| | | function handleAxiosResponseError(error: AxiosError<ErrorResponse, IRequestOptions>) { |
| | | if (error.response.config.url.toLowerCase().includes(RefreshTokenUrl.toLowerCase())) { |
| | | handleLogout(); |
| | | return; |
| | | } |
| | | if (error && error.response) { |
| | | let message = ErrorMessageMap[error.response?.status] ?? '请æ±é误'; |
| | | if (error.response.data?.error?.message) { |
| | | message = error.response.data?.error?.message; |
| | | } |
| | | |
| | | if (error.response?.status === 401) { |
| | | handleLogout(); |
| | | } |
| | | Message.errorMessage(message); |
| | | } |
| | | } |
| | | |
| | | function handleLogout() { |
| | | const userStore = useUserStoreHook(); |
| | | let path = '/'; |
| | | if (userStore.token) { |
| | | path = router.currentRoute.value.fullPath; |
| | | } |
| | | userStore.logout(); |
| | | router.replace(`/login?redirect=${path}`); |
| | | } |
| | | |
| | | export const request = BoleRequest.create(config); |