Browse Source

feat(request): 1、request参数增加skipAuthNavigate, 允许自定义认证过期时,是否需要自动跳转登录页(默认true,与现有逻辑一致); 2、增加errorMessageMode定义,支持自定义错误消息提示类型(默认message与现行一致,增加none错误时不进行弹框提示)

pull/8338/head
雪忆天堂 1 month ago
parent
commit
a828b79022
  1. 9
      apps/web-antd/src/api/request.ts
  2. 19
      apps/web-antd/src/store/auth.ts
  3. 9
      apps/web-antdv-next/src/api/request.ts
  4. 19
      apps/web-antdv-next/src/store/auth.ts
  5. 9
      apps/web-ele/src/api/request.ts
  6. 19
      apps/web-ele/src/store/auth.ts
  7. 9
      apps/web-naive/src/api/request.ts
  8. 19
      apps/web-naive/src/store/auth.ts
  9. 20
      apps/web-tdesign/src/api/request.ts
  10. 18
      apps/web-tdesign/src/store/auth.ts
  11. 17
      packages/effects/request/src/request-client/preset-interceptors.ts
  12. 12
      packages/effects/request/src/request-client/types.ts
  13. 13
      playground/src/api/request.ts
  14. 8
      playground/src/store/auth.ts

9
apps/web-antd/src/api/request.ts

@ -1,7 +1,7 @@
/**
* 该文件可自行根据业务逻辑进行调整
*/
import type { RequestClientOptions } from '@vben/request';
import type { RequestClientConfig, RequestClientOptions } from '@vben/request';
import { useAppConfig } from '@vben/hooks';
import { preferences } from '@vben/preferences';
@ -30,18 +30,19 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) {
/**
* 重新认证逻辑
*/
async function doReAuthenticate() {
async function doReAuthenticate(config?: RequestClientConfig) {
console.warn('Access token or refresh token is invalid or expired. ');
const accessStore = useAccessStore();
const authStore = useAuthStore();
accessStore.setAccessToken(null);
const navigate = !config?.skipAuthNavigate;
if (
preferences.app.loginExpiredMode === 'modal' &&
accessStore.isAccessChecked
) {
accessStore.setAccessToken(null);
accessStore.setLoginExpired(true);
} else {
await authStore.logout();
await authStore.logout(true, navigate);
}
}

19
apps/web-antd/src/store/auth.ts

@ -24,6 +24,7 @@ export const useAuthStore = defineStore('auth', () => {
* 异步处理登录操作
* Asynchronously handle the login process
* @param params 登录表单数据
* @param onSuccess 登录成功回调
*/
async function authLogin(
params: Recordable<any>,
@ -77,14 +78,26 @@ export const useAuthStore = defineStore('auth', () => {
};
}
async function logout(redirect: boolean = true) {
const isLoggingOut = ref(false); // 正在 logout 标识, 防止 /logout 死循环.
async function logout(redirect: boolean = true, navigate: boolean = true) {
if (isLoggingOut.value) return; // 正在登出中, 说明已进入循环, 直接返回.
isLoggingOut.value = true; // 设置 标识
try {
await logoutApi();
} catch {
// 不做任何处理
} finally {
accessStore.setAccessToken(null);
isLoggingOut.value = false; // 重置 标识
resetAllStores();
accessStore.setLoginExpired(false);
}
if (!navigate) {
return;
}
resetAllStores();
accessStore.setLoginExpired(false);
// 回登录页带上当前路由地址
await router.replace({

9
apps/web-antdv-next/src/api/request.ts

@ -1,7 +1,7 @@
/**
* 该文件可自行根据业务逻辑进行调整
*/
import type { RequestClientOptions } from '@vben/request';
import type { RequestClientConfig, RequestClientOptions } from '@vben/request';
import { useAppConfig } from '@vben/hooks';
import { preferences } from '@vben/preferences';
@ -30,18 +30,19 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) {
/**
* 重新认证逻辑
*/
async function doReAuthenticate() {
async function doReAuthenticate(config?: RequestClientConfig) {
console.warn('Access token or refresh token is invalid or expired. ');
const accessStore = useAccessStore();
const authStore = useAuthStore();
accessStore.setAccessToken(null);
const navigate = !config?.skipAuthNavigate;
if (
preferences.app.loginExpiredMode === 'modal' &&
accessStore.isAccessChecked
) {
accessStore.setAccessToken(null);
accessStore.setLoginExpired(true);
} else {
await authStore.logout();
await authStore.logout(true, navigate);
}
}

19
apps/web-antdv-next/src/store/auth.ts

@ -24,6 +24,7 @@ export const useAuthStore = defineStore('auth', () => {
* 异步处理登录操作
* Asynchronously handle the login process
* @param params 登录表单数据
* @param onSuccess 登录成功回调
*/
async function authLogin(
params: Recordable<any>,
@ -77,14 +78,26 @@ export const useAuthStore = defineStore('auth', () => {
};
}
async function logout(redirect: boolean = true) {
const isLoggingOut = ref(false); // 正在 logout 标识, 防止 /logout 死循环.
async function logout(redirect: boolean = true, navigate: boolean = true) {
if (isLoggingOut.value) return; // 正在登出中, 说明已进入循环, 直接返回.
isLoggingOut.value = true; // 设置 标识
try {
await logoutApi();
} catch {
// 不做任何处理
} finally {
accessStore.setAccessToken(null);
isLoggingOut.value = false; // 重置 标识
resetAllStores();
accessStore.setLoginExpired(false);
}
if (!navigate) {
return;
}
resetAllStores();
accessStore.setLoginExpired(false);
// 回登录页带上当前路由地址
await router.replace({

9
apps/web-ele/src/api/request.ts

@ -1,7 +1,7 @@
/**
* 该文件可自行根据业务逻辑进行调整
*/
import type { RequestClientOptions } from '@vben/request';
import type { RequestClientConfig, RequestClientOptions } from '@vben/request';
import { useAppConfig } from '@vben/hooks';
import { preferences } from '@vben/preferences';
@ -30,18 +30,19 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) {
/**
* 重新认证逻辑
*/
async function doReAuthenticate() {
async function doReAuthenticate(config?: RequestClientConfig) {
console.warn('Access token or refresh token is invalid or expired. ');
const accessStore = useAccessStore();
const authStore = useAuthStore();
accessStore.setAccessToken(null);
const navigate = !config?.skipAuthNavigate;
if (
preferences.app.loginExpiredMode === 'modal' &&
accessStore.isAccessChecked
) {
accessStore.setAccessToken(null);
accessStore.setLoginExpired(true);
} else {
await authStore.logout();
await authStore.logout(true, navigate);
}
}

19
apps/web-ele/src/store/auth.ts

@ -24,6 +24,7 @@ export const useAuthStore = defineStore('auth', () => {
* 异步处理登录操作
* Asynchronously handle the login process
* @param params 登录表单数据
* @param onSuccess 登录成功回调
*/
async function authLogin(
params: Recordable<any>,
@ -78,14 +79,26 @@ export const useAuthStore = defineStore('auth', () => {
};
}
async function logout(redirect: boolean = true) {
const isLoggingOut = ref(false); // 正在 logout 标识, 防止 /logout 死循环.
async function logout(redirect: boolean = true, navigate: boolean = true) {
if (isLoggingOut.value) return; // 正在登出中, 说明已进入循环, 直接返回.
isLoggingOut.value = true; // 设置 标识
try {
await logoutApi();
} catch {
// 不做任何处理
} finally {
accessStore.setAccessToken(null);
isLoggingOut.value = false; // 重置 标识
resetAllStores();
accessStore.setLoginExpired(false);
}
if (!navigate) {
return;
}
resetAllStores();
accessStore.setLoginExpired(false);
// 回登录页带上当前路由地址
await router.replace({

9
apps/web-naive/src/api/request.ts

@ -1,7 +1,7 @@
/**
* 该文件可自行根据业务逻辑进行调整
*/
import type { RequestClientOptions } from '@vben/request';
import type { RequestClientConfig, RequestClientOptions } from '@vben/request';
import { useAppConfig } from '@vben/hooks';
import { preferences } from '@vben/preferences';
@ -29,18 +29,19 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) {
/**
* 重新认证逻辑
*/
async function doReAuthenticate() {
async function doReAuthenticate(config?: RequestClientConfig) {
console.warn('Access token or refresh token is invalid or expired. ');
const accessStore = useAccessStore();
const authStore = useAuthStore();
accessStore.setAccessToken(null);
const navigate = !config?.skipAuthNavigate;
if (
preferences.app.loginExpiredMode === 'modal' &&
accessStore.isAccessChecked
) {
accessStore.setAccessToken(null);
accessStore.setLoginExpired(true);
} else {
await authStore.logout();
await authStore.logout(true, navigate);
}
}

19
apps/web-naive/src/store/auth.ts

@ -24,6 +24,7 @@ export const useAuthStore = defineStore('auth', () => {
* 异步处理登录操作
* Asynchronously handle the login process
* @param params 登录表单数据
* @param onSuccess 登录成功回调
*/
async function authLogin(
params: Recordable<any>,
@ -78,14 +79,26 @@ export const useAuthStore = defineStore('auth', () => {
};
}
async function logout(redirect: boolean = true) {
const isLoggingOut = ref(false); // 正在 logout 标识, 防止 /logout 死循环.
async function logout(redirect: boolean = true, navigate: boolean = true) {
if (isLoggingOut.value) return; // 正在登出中, 说明已进入循环, 直接返回.
isLoggingOut.value = true; // 设置 标识
try {
await logoutApi();
} catch {
// 不做任何处理
} finally {
accessStore.setAccessToken(null);
isLoggingOut.value = false; // 重置 标识
resetAllStores();
accessStore.setLoginExpired(false);
}
if (!navigate) {
return;
}
resetAllStores();
accessStore.setLoginExpired(false);
// 回登录页带上当前路由地址
await router.replace({

20
apps/web-tdesign/src/api/request.ts

@ -1,7 +1,7 @@
/**
* 该文件可自行根据业务逻辑进行调整
*/
import type { RequestClientOptions } from '@vben/request';
import type { RequestClientConfig, RequestClientOptions } from '@vben/request';
import { useAppConfig } from '@vben/hooks';
import { preferences } from '@vben/preferences';
@ -29,18 +29,19 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) {
/**
* 重新认证逻辑
*/
async function doReAuthenticate() {
async function doReAuthenticate(config?: RequestClientConfig) {
console.warn('Access token or refresh token is invalid or expired. ');
const accessStore = useAccessStore();
const authStore = useAuthStore();
accessStore.setAccessToken(null);
const navigate = !config?.skipAuthNavigate;
if (
preferences.app.loginExpiredMode === 'modal' &&
accessStore.isAccessChecked
) {
accessStore.setAccessToken(null);
accessStore.setLoginExpired(true);
} else {
await authStore.logout();
await authStore.logout(true, navigate);
}
}
@ -71,13 +72,7 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) {
});
// 处理返回的响应数据格式
client.addResponseInterceptor(
defaultResponseInterceptor({
codeField: 'code',
dataField: 'data',
successCode: 0,
}),
);
client.addResponseInterceptor(defaultResponseInterceptor({}));
// token过期的处理
client.addResponseInterceptor(
@ -96,7 +91,8 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) {
// 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
// 当前mock接口返回的错误字段是 error 或者 message
const responseData = error?.response?.data ?? {};
const errorMessage = responseData?.error ?? responseData?.message ?? '';
const errorMessage =
responseData?.error ?? responseData?.message ?? responseData?.msg ?? '';
// 如果没有错误信息,则会根据状态码进行提示
message.error(errorMessage || msg);
}),

18
apps/web-tdesign/src/store/auth.ts

@ -76,14 +76,26 @@ export const useAuthStore = defineStore('auth', () => {
};
}
async function logout(redirect: boolean = true) {
const isLoggingOut = ref(false); // 正在 logout 标识, 防止 /logout 死循环.
async function logout(redirect: boolean = true, navigate: boolean = true) {
if (isLoggingOut.value) return; // 正在登出中, 说明已进入循环, 直接返回.
isLoggingOut.value = true; // 设置 标识
try {
await logoutApi();
} catch {
// 不做任何处理
} finally {
accessStore.setAccessToken(null);
isLoggingOut.value = false; // 重置 标识
resetAllStores();
accessStore.setLoginExpired(false);
}
if (!navigate) {
return;
}
resetAllStores();
accessStore.setLoginExpired(false);
// 回登录页带上当前路由地址
await router.replace({

17
packages/effects/request/src/request-client/preset-interceptors.ts

@ -1,5 +1,9 @@
import type { RequestClient } from './request-client';
import type { MakeErrorMessageFn, ResponseInterceptorConfig } from './types';
import type {
MakeErrorMessageFn,
RequestClientConfig,
ResponseInterceptorConfig,
} from './types';
import { $t } from '@vben/locales';
import { isFunction } from '@vben/utils';
@ -52,7 +56,7 @@ export const authenticateResponseInterceptor = ({
formatToken,
}: {
client: RequestClient;
doReAuthenticate: () => Promise<void>;
doReAuthenticate: (config?: RequestClientConfig) => Promise<void>;
doRefreshToken: () => Promise<string>;
enableRefreshToken: boolean;
formatToken: (token: string) => null | string;
@ -67,7 +71,7 @@ export const authenticateResponseInterceptor = ({
// 判断是否启用了 refreshToken 功能
// 如果没有启用或者已经是重试请求了,直接跳转到重新登录
if (!enableRefreshToken || config.__isRetryRequest) {
await doReAuthenticate();
await doReAuthenticate(config);
throw error;
}
// 如果正在刷新 token,则将请求加入队列,等待刷新完成
@ -99,7 +103,7 @@ export const authenticateResponseInterceptor = ({
client.refreshTokenQueue.forEach((callback) => callback(''));
client.refreshTokenQueue = [];
console.error('Refresh token failed, please login again.');
await doReAuthenticate();
await doReAuthenticate(config);
throw refreshError;
} finally {
@ -118,6 +122,11 @@ export const errorMessageResponseInterceptor = (
return Promise.reject(error);
}
// 如果配置了错误消息提示模式为 none,则跳过错误提示
if (error?.config?.errorMessageMode === 'none') {
return Promise.reject(error);
}
const err: string = error?.toString?.() ?? '';
let errMsg = '';
if (err?.includes('Network Error')) {

12
packages/effects/request/src/request-client/types.ts

@ -26,6 +26,18 @@ type ExtendOptions<T = any> = {
* - data: 解构响应的BODY数据,只返回其中的data节点数据(会检查status和code是否为成功状态)。
*/
responseReturn?: 'body' | 'data' | 'raw';
/**
* 认证过期时是否跳过自动跳转登录页。
* - true: 仅清除登录态,不跳转
* - false / 不传: 清除登录态并跳转登录页
*/
skipAuthNavigate?: boolean;
/**
* 错误消息提示模式。
* - message: 使用 message 提示(默认)
* - none: 不显示错误提示
*/
errorMessageMode?: 'message' | 'none';
};
type RequestClientConfig<T = any> = AxiosRequestConfig<T> & ExtendOptions<T>;

13
playground/src/api/request.ts

@ -1,7 +1,11 @@
/**
* 该文件可自行根据业务逻辑进行调整
*/
import type { AxiosResponseHeaders, RequestClientOptions } from '@vben/request';
import type {
AxiosResponseHeaders,
RequestClientConfig,
RequestClientOptions,
} from '@vben/request';
import { useAppConfig } from '@vben/hooks';
import { preferences } from '@vben/preferences';
@ -44,18 +48,19 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) {
/**
* 重新认证逻辑
*/
async function doReAuthenticate() {
async function doReAuthenticate(config?: RequestClientConfig) {
console.warn('Access token or refresh token is invalid or expired. ');
const accessStore = useAccessStore();
const authStore = useAuthStore();
accessStore.setAccessToken(null);
const navigate = !config?.skipAuthNavigate;
if (
preferences.app.loginExpiredMode === 'modal' &&
accessStore.isAccessChecked
) {
accessStore.setAccessToken(null);
accessStore.setLoginExpired(true);
} else {
await authStore.logout();
await authStore.logout(true, navigate);
}
}

8
playground/src/store/auth.ts

@ -79,7 +79,7 @@ export const useAuthStore = defineStore('auth', () => {
const isLoggingOut = ref(false); // 正在 logout 标识, 防止 /logout 死循环.
async function logout(redirect: boolean = true) {
async function logout(redirect: boolean = true, navigate: boolean = true) {
if (isLoggingOut.value) return; // 正在登出中, 说明已进入循环, 直接返回.
isLoggingOut.value = true; // 设置 标识
@ -88,12 +88,16 @@ export const useAuthStore = defineStore('auth', () => {
} catch {
// 不做任何处理
} finally {
accessStore.setAccessToken(null);
isLoggingOut.value = false; // 重置 标识
resetAllStores();
accessStore.setLoginExpired(false);
}
if (!navigate) {
return;
}
// 回登录页带上当前路由地址
await router.replace({
path: LOGIN_PATH,

Loading…
Cancel
Save