feat: refactor request

This commit is contained in:
vben
2024-06-02 20:50:51 +08:00
parent ce0c3834ed
commit f95cc80895
29 changed files with 1096 additions and 265 deletions

View File

@@ -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:*",

View File

@@ -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';
/**
* 登录

View File

@@ -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<T = any> {
code: number;
message: string;
result: T;
}
// 用于存储每个请求的标识和取消函数
const pendingMap = new Map<string, AbortController>();
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<HttpResponse>) => {
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<T>(url: string, config: AxiosRequestConfig): Promise<T> {
try {
const response: AxiosResponse<T> = await axiosInstance({
url,
...config,
});
return response as T;
} catch (error: any) {
throw error.response ? error.response.data : error;
}
}
export { abortAllRequest, request };

View File

@@ -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<T = any> {
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<HttpResponse>) => {
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 };

View File

@@ -1,12 +1,12 @@
<script lang="ts" setup>
import type { LoginAndRegisterParams } from '@vben/common-ui';
import { useRequest } from '@vben-core/request';
import { useAccessStore } from '@vben-core/stores';
import { getUserInfo, userLogin } from '@/apis';
import { AuthenticationLogin } from '@vben/common-ui';
import { $t } from '@vben/locales';
import { useRequest } from '@vben/request';
import { notification } from 'ant-design-vue';
import { computed } from 'vue';
import { useRouter } from 'vue-router';