Browse Source

🎨 feat: 统一处理请求结果.

pull/1084/head
colin 1 year ago
parent
commit
97cfa002b0
  1. 40
      apps/vben5/apps/app-antd/src/adapter/request/index.ts
  2. 8
      apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json
  3. 8
      apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json
  4. 1
      apps/vben5/packages/@abp/account/src/hooks/index.ts
  5. 16
      apps/vben5/packages/@abp/account/src/hooks/useOAuthError.ts
  6. 1
      apps/vben5/packages/@abp/account/src/index.ts
  7. 14
      apps/vben5/packages/@abp/core/src/types/error.ts
  8. 1
      apps/vben5/packages/@abp/core/src/types/index.ts
  9. 22
      apps/vben5/packages/@abp/openiddict/src/components/applications/ApplicationModal.vue
  10. 2
      apps/vben5/packages/@abp/request/src/hooks/index.ts
  11. 28
      apps/vben5/packages/@abp/request/src/hooks/useErrorFormat.ts
  12. 35
      apps/vben5/packages/@abp/request/src/hooks/useWrapperResult.ts

40
apps/vben5/apps/app-antd/src/adapter/request/index.ts

@ -1,4 +1,3 @@
import { $t } from '@vben/locales';
import { preferences } from '@vben/preferences';
import {
authenticateResponseInterceptor,
@ -6,8 +5,8 @@ import {
} from '@vben/request';
import { useAccessStore } from '@vben/stores';
import { useTokenApi } from '@abp/account';
import { requestClient } from '@abp/request';
import { useOAuthError, useTokenApi } from '@abp/account';
import { requestClient, useWrapperResult } from '@abp/request';
import { message } from 'ant-design-vue';
import { useAuthStore } from '#/store';
@ -38,13 +37,17 @@ export function initRequestClient() {
async function doRefreshToken() {
const accessStore = useAccessStore();
if (accessStore.refreshToken) {
const { accessToken, tokenType, refreshToken } = await refreshTokenApi({
refreshToken: accessStore.refreshToken,
});
const newToken = `${tokenType} ${accessToken}`;
accessStore.setAccessToken(newToken);
accessStore.setRefreshToken(refreshToken);
return newToken;
try {
const { accessToken, tokenType, refreshToken } = await refreshTokenApi({
refreshToken: accessStore.refreshToken,
});
const newToken = `${tokenType} ${accessToken}`;
accessStore.setAccessToken(newToken);
accessStore.setRefreshToken(refreshToken);
return newToken;
} catch {
console.warn('The refresh token has expired or is unavailable.');
}
}
return '';
}
@ -68,17 +71,11 @@ export function initRequestClient() {
// response数据解构
requestClient.addResponseInterceptor<any>({
fulfilled: (response) => {
const { data, status, headers } = response;
if (headers._abpwrapresult === 'true') {
const { code, result, message, details } = data;
const hasSuccess = data && Reflect.has(data, 'code') && code === '0';
if (hasSuccess) {
return result;
}
const content = details || message;
const { data, status } = response;
const { hasWrapResult, getData } = useWrapperResult(response);
throw Object.assign({}, response, { response, message: content });
if (hasWrapResult()) {
return getData();
}
if (status >= 200 && status < 400) {
@ -107,7 +104,8 @@ export function initRequestClient() {
// 当前mock接口返回的错误字段是 error 或者 message
const responseData = error?.response?.data ?? {};
if (responseData?.error_description) {
message.error($t(`abp.oauth.${responseData.error_description}`) || msg);
const { formatError } = useOAuthError();
message.error(formatError(responseData) || msg);
return;
}
const errorMessage = responseData?.error ?? responseData?.message ?? '';

8
apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json

@ -1,10 +1,10 @@
{
"title": "Abp Framework",
"oauth": {
"Invalid username or password!": "Invalid username or password!",
"Invalid authenticator code!": "Invalid authenticator code!",
"The specified refresh token is no longer valid.": "The session has expired. Please log in again!",
"RequiresTwoFactor": "Requires Two Factor",
"invalidUserNameOrPassword": "Invalid username or password!!",
"invalidAuthenticatorCode": "Invalid authenticator code!",
"sessionExpired": "The session has expired. Please log in again!",
"requiresTwoFactor": "Requires Two Factor",
"twoFactor": {
"title": "Two Factor",
"authenticator": "Authenticator",

8
apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json

@ -1,10 +1,10 @@
{
"title": "Abp框架",
"oauth": {
"Invalid username or password!": "用户名或密码错误!",
"Invalid authenticator code!": "无效的验证器代码!",
"The specified refresh token is no longer valid.": "会话已过期,请重新登陆!",
"RequiresTwoFactor": "需要二次认证",
"invalidUserNameOrPassword": "用户名或密码错误!",
"invalidAuthenticatorCode": "无效的验证器代码!",
"sessionExpired": "会话已过期,请重新登陆!",
"requiresTwoFactor": "需要二次认证",
"twoFactor": {
"title": "二次认证",
"authenticator": "验证方式",

1
apps/vben5/packages/@abp/account/src/hooks/index.ts

@ -0,0 +1 @@
export * from './useOAuthError';

16
apps/vben5/packages/@abp/account/src/hooks/useOAuthError.ts

@ -0,0 +1,16 @@
interface OAuthError {
error: string;
error_description?: string;
error_uri?: string;
}
export function useOAuthError() {
function formatError(error: OAuthError) {
// TODO: 解决oauth消息国际化.
return error.error_description;
}
return {
formatError,
};
}

1
apps/vben5/packages/@abp/account/src/index.ts

@ -1,3 +1,4 @@
export * from './api';
export * from './components';
export * from './hooks';
export * from './types';

14
apps/vben5/packages/@abp/core/src/types/error.ts

@ -0,0 +1,14 @@
interface RemoteServiceValidationErrorInfo {
members: string[];
message: string;
}
interface RemoteServiceErrorInfo {
code?: string;
data?: Record<string, any>;
details?: string;
message?: string;
validationErrors?: RemoteServiceValidationErrorInfo[];
}
export type { RemoteServiceErrorInfo };

1
apps/vben5/packages/@abp/core/src/types/index.ts

@ -1,4 +1,5 @@
export * from './dto';
export * from './error';
export * from './features';
export * from './global';
export * from './localization';

22
apps/vben5/packages/@abp/openiddict/src/components/applications/ApplicationModal.vue

@ -26,6 +26,7 @@ import { $t } from '@vben/locales';
import { DownOutlined } from '@ant-design/icons-vue';
import {
Checkbox,
Dropdown,
Form,
Input,
@ -138,6 +139,9 @@ const getSupportScopes = computed((): TransferItem[] => {
};
});
});
const getPkceEnabled = computed(() => {
return formModel.value.requirements?.includes('pkce');
});
const { discoveryApi } = useOpenIdApi();
const { cancel, createApi, getApi, updateApi } = useApplicationsApi();
@ -266,6 +270,13 @@ function onUriDelete(uri: string) {
}
}
}
function onPkceChange(checked: boolean) {
const requirements: string[] = [];
if (checked) {
requirements.push('pkce');
}
formModel.value.requirements = requirements;
}
</script>
<template>
@ -401,6 +412,17 @@ function onUriDelete(uri: string) {
</TabPane>
<!-- 授权 -->
<TabPane key="authorize" :tab="$t('AbpOpenIddict.Authorizations')">
<FormItem
:label="$t('AbpOpenIddict.Requirements:PKCE')"
name="requirements"
>
<Checkbox
:checked="getPkceEnabled"
@change="(e) => onPkceChange(e.target.checked)"
>
{{ $t('AbpOpenIddict.Requirements:PKCE') }}
</Checkbox>
</FormItem>
<FormItem
:label="$t('AbpOpenIddict.DisplayName:Endpoints')"
:label-col="{ span: 4 }"

2
apps/vben5/packages/@abp/request/src/hooks/index.ts

@ -1 +1,3 @@
export * from './useErrorFormat';
export * from './useRequest';
export * from './useWrapperResult';

28
apps/vben5/packages/@abp/request/src/hooks/useErrorFormat.ts

@ -0,0 +1,28 @@
import type { RemoteServiceErrorInfo } from '@abp/core';
import type { AxiosResponse } from 'axios';
export function useErrorFormat(response: AxiosResponse) {
const _defaultErrorHeaderKey: string = '_abperrorformat';
const { data, headers } = response;
/** 是否请求错误 */
function hasError(): boolean {
return headers[_defaultErrorHeaderKey] === 'true';
}
/** 如果请求错误,抛出异常 */
function throwIfError(): void {
if (!hasError()) return;
const errorJson = data.error as RemoteServiceErrorInfo;
let errorMessage = errorJson.message;
if (errorJson.validationErrors) {
errorMessage += errorJson.validationErrors
.map((error) => error.message)
.join('\n');
}
throw Object.assign({}, response, { message: errorMessage, response });
}
return {
hasError,
throwIfError,
};
}

35
apps/vben5/packages/@abp/request/src/hooks/useWrapperResult.ts

@ -0,0 +1,35 @@
import type { AxiosResponse } from 'axios';
import { useErrorFormat } from './useErrorFormat';
export function useWrapperResult(response: AxiosResponse) {
const { hasError, throwIfError: throwIfAbpError } = useErrorFormat(response);
const _defaultWrapperHeaderKey: string = '_abpwrapresult';
const { data, headers } = response;
/** 是否已包装结果 */
function hasWrapResult(): boolean {
return headers[_defaultWrapperHeaderKey] === 'true' || hasError();
}
/** 获取包装结果 */
function getData(): any {
throwIfError();
return data.result;
}
/** 如果请求错误,抛出异常 */
function throwIfError(): void {
throwIfAbpError();
const { code, details, message } = data;
const hasSuccess = data && Reflect.has(data, 'code') && code === '0';
if (!hasSuccess) {
const content = details || message;
throw Object.assign({}, response, { message: content, response });
}
}
return {
getData,
hasWrapResult,
};
}
Loading…
Cancel
Save