From f95cc80895d7b573dc5907148c042d8c7abd0c66 Mon Sep 17 00:00:00 2001 From: vben Date: Sun, 2 Jun 2024 20:50:51 +0800 Subject: [PATCH] feat: refactor request --- .vscode/settings.json | 4 +- apps/antd-view/package.json | 2 +- apps/antd-view/src/apis/modules/user.ts | 2 +- apps/antd-view/src/apis/request.ts | 153 ------------ apps/antd-view/src/forward/request.ts | 81 ++++++ .../views/_essential/authentication/login.vue | 2 +- package.json | 2 +- .../forward}/request/build.config.ts | 0 .../forward}/request/package.json | 9 +- .../@vben-core/forward/request/src/index.ts | 3 + .../request/src/request-client/index.ts | 3 + .../request-client/modules/canceler.test.ts | 127 ++++++++++ .../src/request-client/modules/canceler.ts | 52 ++++ .../request-client/modules/downloader.test.ts | 84 +++++++ .../src/request-client/modules/downloader.ts | 30 +++ .../src/request-client/modules/interceptor.ts | 33 +++ .../request-client/modules/uploader.test.ts | 118 +++++++++ .../src/request-client/modules/uploader.ts | 32 +++ .../src/request-client/request-client.test.ts | 97 ++++++++ .../src/request-client/request-client.ts | 179 ++++++++++++++ .../request/src/request-client/types.ts | 24 ++ .../request/src/request-client/util.test.ts | 25 ++ .../request/src/request-client/util.ts | 7 + .../forward}/request/src/use-request.ts | 0 .../forward}/request/tsconfig.json | 0 packages/request/src/index.ts | 1 - packages/styles/src/common/entry.scss | 51 ++++ pnpm-lock.yaml | 232 ++++++++++-------- vben-admin.code-workspace | 8 +- 29 files changed, 1096 insertions(+), 265 deletions(-) delete mode 100644 apps/antd-view/src/apis/request.ts create mode 100644 apps/antd-view/src/forward/request.ts rename packages/{ => @vben-core/forward}/request/build.config.ts (100%) rename packages/{ => @vben-core/forward}/request/package.json (80%) create mode 100644 packages/@vben-core/forward/request/src/index.ts create mode 100644 packages/@vben-core/forward/request/src/request-client/index.ts create mode 100644 packages/@vben-core/forward/request/src/request-client/modules/canceler.test.ts create mode 100644 packages/@vben-core/forward/request/src/request-client/modules/canceler.ts create mode 100644 packages/@vben-core/forward/request/src/request-client/modules/downloader.test.ts create mode 100644 packages/@vben-core/forward/request/src/request-client/modules/downloader.ts create mode 100644 packages/@vben-core/forward/request/src/request-client/modules/interceptor.ts create mode 100644 packages/@vben-core/forward/request/src/request-client/modules/uploader.test.ts create mode 100644 packages/@vben-core/forward/request/src/request-client/modules/uploader.ts create mode 100644 packages/@vben-core/forward/request/src/request-client/request-client.test.ts create mode 100644 packages/@vben-core/forward/request/src/request-client/request-client.ts create mode 100644 packages/@vben-core/forward/request/src/request-client/types.ts create mode 100644 packages/@vben-core/forward/request/src/request-client/util.test.ts create mode 100644 packages/@vben-core/forward/request/src/request-client/util.ts rename packages/{ => @vben-core/forward}/request/src/use-request.ts (100%) rename packages/{ => @vben-core/forward}/request/tsconfig.json (100%) delete mode 100644 packages/request/src/index.ts create mode 100644 packages/styles/src/common/entry.scss diff --git a/.vscode/settings.json b/.vscode/settings.json index a009863e..02c88c7e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -195,8 +195,8 @@ "explorer.fileNesting.enabled": true, "explorer.fileNesting.expand": false, "explorer.fileNesting.patterns": { - "*.ts": "$(capture).test.ts, $(capture).test.tsx", - "*.tsx": "$(capture).test.ts, $(capture).test.tsx", + "*.ts": "$(capture).test.ts, $(capture).test.tsx, $(capture).spec.ts, $(capture).spec.tsx, $(capture).d.ts", + "*.tsx": "$(capture).test.ts, $(capture).test.tsx, $(capture).spec.ts, $(capture).spec.tsx,$(capture).d.ts", "*.env": "$(capture).env.*", "README.md": "README*,CHANGELOG*,LICENSE,CNAME", "package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,.gitattributes,.gitignore,.gitpod.yml,.npmrc,.browserslistrc,.node-version,.git*,.tazerc.json", diff --git a/apps/antd-view/package.json b/apps/antd-view/package.json index 7b376999..9075bb14 100644 --- a/apps/antd-view/package.json +++ b/apps/antd-view/package.json @@ -24,6 +24,7 @@ "dependencies": { "@vben-core/helpers": "workspace:*", "@vben-core/preferences": "workspace:*", + "@vben-core/request": "workspace:*", "@vben-core/stores": "workspace:*", "@vben/common-ui": "workspace:*", "@vben/constants": "workspace:*", @@ -31,7 +32,6 @@ "@vben/icons": "workspace:*", "@vben/layouts": "workspace:*", "@vben/locales": "workspace:*", - "@vben/request": "workspace:*", "@vben/styles": "workspace:*", "@vben/types": "workspace:*", "@vben/utils": "workspace:*", diff --git a/apps/antd-view/src/apis/modules/user.ts b/apps/antd-view/src/apis/modules/user.ts index b24a720a..3bf9d16a 100644 --- a/apps/antd-view/src/apis/modules/user.ts +++ b/apps/antd-view/src/apis/modules/user.ts @@ -1,7 +1,7 @@ import type { UserApiType } from '@/apis/types'; import type { UserInfo } from '@vben/types'; -import { request } from '@/apis/request'; +import { request } from '@/forward/request'; /** * 登录 diff --git a/apps/antd-view/src/apis/request.ts b/apps/antd-view/src/apis/request.ts deleted file mode 100644 index 4a1d19a9..00000000 --- a/apps/antd-view/src/apis/request.ts +++ /dev/null @@ -1,153 +0,0 @@ -/** - * 该文件可自行根据业务逻辑进行调整 - */ - -import { useAccessStore } from '@vben-core/stores'; - -import { message } from 'ant-design-vue'; -import axios, { - AxiosError, - AxiosRequestConfig, - AxiosResponse, - InternalAxiosRequestConfig, -} from 'axios'; - -// 后端需要的 token 存放在header内的key字段 -// 可以根据自己的需要修改 -const REQUEST_HEADER_TOKEN_KEY = 'Authorization'; - -type HttpConfig = InternalAxiosRequestConfig; - -interface HttpResponse { - code: number; - message: string; - result: T; -} - -// 用于存储每个请求的标识和取消函数 -const pendingMap = new Map(); - -const getPendingUrl = (config: AxiosRequestConfig): string => { - return [config.method, config.url].join('&'); -}; - -/** - * 添加请求 - * @param config 请求配置 - */ -function addRequestSignal(config: AxiosRequestConfig): void { - abortRequest(config); - const url = getPendingUrl(config); - const controller = new AbortController(); - config.signal = config.signal || controller.signal; - if (!pendingMap.has(url)) { - // 如果当前请求不在等待中,将其添加到等待中 - pendingMap.set(url, controller); - } -} - -/** - * 清除所有等待中的请求 - */ -function abortAllRequest() { - pendingMap.forEach((abortController) => { - if (abortController) { - abortController.abort(); - } - }); - pendingMap.clear(); -} - -/** - * 移除请求 - * @param config 请求配置 - */ -function abortRequest(config: AxiosRequestConfig): void { - if (!config) { - return; - } - const url = getPendingUrl(config); - if (pendingMap.has(url)) { - // 如果当前请求在等待中,取消它并将其从等待中移除 - const abortController = pendingMap.get(url); - if (abortController) { - abortController.abort(url); - } - pendingMap.delete(url); - } -} - -const axiosInstance = axios.create({ - // .env 环境获取请求地址 - baseURL: import.meta.env.VITE_GLOB_API_URL, - headers: { - 'Content-Type': 'application/json;charset=utf-8', - }, - timeout: 10 * 1000, -}); - -// 请求拦截器 -axiosInstance.interceptors.request.use( - (config: HttpConfig) => { - addRequestSignal(config); - - // 携带 getAccessToken 在请求头 - const accessStore = useAccessStore(); - const getAccessToken = accessStore.getAccessToken; - - if (getAccessToken) { - config.headers[REQUEST_HEADER_TOKEN_KEY] = getAccessToken; - } - return config; - }, - (error: AxiosError) => { - return Promise.reject(error); - }, -); - -// 添加响应拦截器 -axiosInstance.interceptors.response.use( - (response: AxiosResponse) => { - const { data: responseData, status } = response; - const { code, message: msg, result } = responseData; - abortRequest(response.config); - - if (status === 200 && code === 0) { - return result; - } else { - message.error(msg); - throw new Error(msg); - } - }, - (error: any) => { - if (axios.isCancel(error)) { - return Promise.reject(error); - } - - const err: string = error?.toString?.() ?? ''; - let errMsg = ''; - if (err?.includes('Network Error')) { - errMsg = '网络错误。'; - } else if (error?.message?.includes?.('timeout')) { - errMsg = '请求超时。'; - } else { - errMsg = error?.response?.data?.error?.message ?? ''; - } - message.error(errMsg); - return Promise.reject(error); - }, -); - -async function request(url: string, config: AxiosRequestConfig): Promise { - try { - const response: AxiosResponse = await axiosInstance({ - url, - ...config, - }); - return response as T; - } catch (error: any) { - throw error.response ? error.response.data : error; - } -} - -export { abortAllRequest, request }; diff --git a/apps/antd-view/src/forward/request.ts b/apps/antd-view/src/forward/request.ts new file mode 100644 index 00000000..fcadffb7 --- /dev/null +++ b/apps/antd-view/src/forward/request.ts @@ -0,0 +1,81 @@ +/** + * 该文件可自行根据业务逻辑进行调整 + */ + +import type { AxiosResponse } from '@vben-core/request'; + +import { RequestClient, isCancelError } from '@vben-core/request'; +import { useAccessStore } from '@vben-core/stores'; + +import { message } from 'ant-design-vue'; + +interface HttpResponse { + code: number; + message: string; + result: T; +} + +function createRequestClient() { + const client = new RequestClient({ + baseURL: import.meta.env.VITE_GLOB_API_URL, + // 为每个请求携带 Authorization + makeAuthorization: () => { + return { + handle: () => { + const accessStore = useAccessStore(); + return accessStore.getAccessToken; + }, + // 默认 + // key: 'Authorization', + }; + }, + }); + setupRequestInterceptors(client); + const request = client.request.bind(client); + const get = client.get.bind(client); + const post = client.post.bind(client); + return { + get, + post, + request, + }; +} + +function setupRequestInterceptors(client: RequestClient) { + client.addResponseInterceptor( + (response: AxiosResponse) => { + const { data: responseData, status } = response; + const { code, message: msg, result } = responseData; + if (status === 200 && code === 0) { + return result; + } else { + message.error(msg); + throw new Error(msg); + } + }, + (error: any) => { + if (isCancelError(error)) { + return Promise.reject(error); + } + + const err: string = error?.toString?.() ?? ''; + let errMsg = ''; + if (err?.includes('Network Error')) { + errMsg = '网络错误。'; + } else if (error?.message?.includes?.('timeout')) { + errMsg = '请求超时。'; + } else { + errMsg = error?.response?.data?.error?.message ?? ''; + } + message.error(errMsg); + return Promise.reject(error); + }, + ); +} + +const { request } = createRequestClient(); + +// 其他配置的请求方法 +// const { request: xxxRequest } = createRequest(); + +export { request }; diff --git a/apps/antd-view/src/views/_essential/authentication/login.vue b/apps/antd-view/src/views/_essential/authentication/login.vue index a0468276..2131a2fa 100644 --- a/apps/antd-view/src/views/_essential/authentication/login.vue +++ b/apps/antd-view/src/views/_essential/authentication/login.vue @@ -1,12 +1,12 @@