Files
xk-admin/apps/web-antd/src/api/request.ts

257 lines
7.0 KiB
TypeScript
Raw Normal View History

2024-06-02 20:50:51 +08:00
/**
*
*/
import type { HttpResponse } from '@vben/request';
import { useAppConfig } from '@vben/hooks';
import { preferences } from '@vben/preferences';
import {
authenticateResponseInterceptor,
errorMessageResponseInterceptor,
RequestClient,
} from '@vben/request';
2024-07-30 21:10:28 +08:00
import { useAccessStore } from '@vben/stores';
2024-06-02 20:50:51 +08:00
import { message } from 'ant-design-vue';
// eslint-disable-next-line n/no-extraneous-import
import { Base64 } from 'js-base64';
2024-06-02 20:50:51 +08:00
import {
ENCRYPT_FIELDS,
isSensitiveField,
maskSensitiveField,
TM_TEXT_SUFFIX,
} from '#/constants/sensitive-fields';
2024-07-30 21:10:28 +08:00
import { useAuthStore } from '#/store';
2024-06-02 20:50:51 +08:00
import { refreshTokenApi } from './core';
import {downloadByData} from "#/util/tool";
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
function createRequestClient(baseURL: string) {
2024-06-02 20:50:51 +08:00
const client = new RequestClient({
baseURL,
});
/**
*
*/
async function doReAuthenticate() {
const accessStore = useAccessStore();
const authStore = useAuthStore();
accessStore.setAccessToken(null);
if (
preferences.app.loginExpiredMode === 'modal' &&
accessStore.isAccessChecked
) {
accessStore.setLoginExpired(true);
} else {
await authStore.logout(true);
}
}
/**
* token逻辑
*/
async function doRefreshToken() {
const accessStore = useAccessStore();
const resp = await refreshTokenApi();
const newToken = resp.data;
accessStore.setAccessToken(newToken);
return newToken;
}
function formatToken(token: null | string) {
return token ? `Bearer ${token}` : null;
}
// 请求头处理
client.addRequestInterceptor({
fulfilled: async (config) => {
const accessStore = useAccessStore();
config.headers.Authorization = formatToken(accessStore.accessToken);
config.headers['Accept-Language'] = preferences.app.locale;
2025-03-07 08:18:45 +08:00
// 加密
// eslint-disable-next-line eqeqeq
if (config.data != null) {
config.data = getRes(config.data, false);
}
// eslint-disable-next-line eqeqeq
if (config.params != null) {
config.params = getRes(config.params, false);
}
return config;
},
2024-06-02 20:50:51 +08:00
});
// response数据解构
client.addResponseInterceptor<HttpResponse>({
fulfilled: (response) => {
const { data: responseData, status } = response;
2025-03-07 08:18:45 +08:00
// 这里吧 data 换成了 result
const { code, result } = responseData;
// 判断返回状态码
if (status >= 200 && status <= 500) {
2025-03-07 08:18:45 +08:00
switch (code) {
case 0: {
// 解密
// eslint-disable-next-line eqeqeq
if (result != null) {
return getRes(result);
}
return result;
}
case 401: {
response.status = 401;
2025-03-07 08:18:45 +08:00
// 401 错误,需要重新登录
authenticateResponseInterceptor({
client,
doReAuthenticate,
doRefreshToken,
enableRefreshToken: preferences.app.enableRefreshToken,
formatToken,
});
break;
2025-03-07 08:18:45 +08:00
}
case 403:
case 404:
case 500: {
throw Object.assign({}, response, { response });
}
}
2025-05-05 13:06:10 +08:00
}
if (response.data instanceof Blob) {
return response;
}
2025-05-05 13:06:10 +08:00
throw Object.assign({}, response, { response });
},
});
// token过期的处理
client.addResponseInterceptor(
authenticateResponseInterceptor({
client,
doReAuthenticate,
doRefreshToken,
enableRefreshToken: preferences.app.enableRefreshToken,
formatToken,
}),
);
// 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里
client.addResponseInterceptor(
errorMessageResponseInterceptor((msg: string, error) => {
// 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
// 当前mock接口返回的错误字段是 error 或者 message
const responseData = error?.response?.data ?? {};
const errorMessage = responseData?.error ?? responseData?.message ?? '';
// 如果没有错误信息,则会根据状态码进行提示
message.error(errorMessage || msg);
}),
);
return client;
2024-06-02 20:50:51 +08:00
}
export const requestClient = createRequestClient(apiURL);
export const baseRequestClient = new RequestClient({ baseURL: apiURL });
2025-03-07 08:18:45 +08:00
/**
*
* @param obj
* @param isDecode
*/
function getRes(obj: any, isDecode = true) {
try {
if (obj == null || typeof obj !== 'object') {
return obj;
}
2025-03-07 08:18:45 +08:00
for (const key in obj) {
// 跳过脱敏展示字段,避免重复处理
if (key.endsWith(TM_TEXT_SUFFIX)) {
continue;
}
2025-03-07 08:18:45 +08:00
if (Array.isArray(obj[key])) {
obj[key] = getRes(obj[key], isDecode);
} else if (obj[key] !== null && typeof obj[key] === 'object') {
obj[key] = getRes(obj[key], isDecode);
} else if (ENCRYPT_FIELDS.has(key)) {
let plainValue = isDecode
? customBase64Decode(obj[key], key)
: customBase64Encode(obj[key]);
const isGarbled =
// eslint-disable-next-line no-control-regex
/[\u0000-\u0008\v\f\u000E-\u001F\u007F-\u00FF\u{E000}-\u{F8FF}]/u.test(
plainValue,
);
if (isGarbled) {
plainValue = obj[key];
}
obj[key] = plainValue;
// 解密后写入脱敏展示字段,明文保留在原字段
if (isDecode && isSensitiveField(key)) {
obj[key + TM_TEXT_SUFFIX] = maskSensitiveField(key, plainValue);
2025-03-07 08:18:45 +08:00
}
}
}
return obj;
} catch (error) {
console.error('错误', error);
return obj;
}
}
/**
*
* @param data
* @param key
2025-03-07 08:18:45 +08:00
*/
function customBase64Decode(data: string, key = '') {
2025-03-07 08:18:45 +08:00
// return data
try {
// 第一次Base64解码
const decodedFirst = Base64.decode(data);
// 截取从第30个字符开始往后的内容
const subStr = decodedFirst.slice(30);
// 第二次Base64解码
const base64 = Base64.decode(subStr);
return base64.length > 0 ? base64 : data;
} catch (error) {
console.error(
`解码Base64字符串【${data}】时发生错误KEY${key}`,
error,
);
2025-03-07 08:18:45 +08:00
return data;
}
}
/**
*
* @param data
*/
function customBase64Encode(data: string) {
// eslint-disable-next-line eqeqeq
if (data == null || data === '') return data;
// return data
try {
// 和加密方法差不多但是是要生成30个随机字符
let randomString = '';
for (let i = 0; i < 30; i++) {
const randomChar = String.fromCodePoint(
Math.floor(Math.random() * (126 - 32 + 1)) + 32,
);
randomString += randomChar;
}
const encoded = Base64.encode(data);
return Base64.encode(randomString + encoded);
} catch (error) {
console.error(`加密字符串【${data}】时发生错误`, error);
return data;
}
}