Browse Source

feat(request): 落地统一响应字段约定 ResultFieldEnum

- 新增 ResultFieldEnum(code/data/message),响应字段名统一收敛到 @vben/constants
- 三个预置拦截器默认值与业务 code 读取改用枚举,并透传 codeField 配置支持自定义字段
- 补充各拦截器的方法级 JSDoc 与参数注释
pull/8337/head
雪忆天堂 4 weeks ago
parent
commit
ccc49a2482
  1. 5
      apps/web-antd/src/api/request.ts
  2. 5
      apps/web-antdv-next/src/api/request.ts
  3. 5
      apps/web-ele/src/api/request.ts
  4. 5
      apps/web-naive/src/api/request.ts
  5. 5
      apps/web-tdesign/src/api/request.ts
  6. 5
      docs/src/en/guide/essentials/server.md
  7. 5
      docs/src/guide/essentials/server.md
  8. 7
      packages/constants/src/http.enum.ts
  9. 41
      packages/effects/request/src/request-client/preset-interceptors.ts
  10. 5
      playground/src/api/request.ts

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

@ -3,6 +3,7 @@
*/ */
import type { RequestClientOptions } from '@vben/request'; import type { RequestClientOptions } from '@vben/request';
import { ResultFieldEnum } from '@vben/constants';
import { useAppConfig } from '@vben/hooks'; import { useAppConfig } from '@vben/hooks';
import { preferences } from '@vben/preferences'; import { preferences } from '@vben/preferences';
import { import {
@ -89,10 +90,10 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) {
client.addResponseInterceptor( client.addResponseInterceptor(
errorMessageResponseInterceptor((msg: string, error) => { errorMessageResponseInterceptor((msg: string, error) => {
// 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
// 当前mock接口返回的错误字段是 error 或者 message // 默认从统一响应约定的 message 字段中提取错误信息,如后端使用其他字段(如 error、msg),可在此自定义
const responseData = error?.response?.data ?? {}; const responseData = error?.response?.data ?? {};
const errorMessage = const errorMessage =
responseData?.error ?? responseData?.message ?? responseData?.msg ?? ''; responseData?.[ResultFieldEnum.MESSAGE] ?? responseData?.error ?? '';
// 如果没有错误信息,则会根据状态码进行提示 // 如果没有错误信息,则会根据状态码进行提示
message.error(errorMessage || msg); message.error(errorMessage || msg);
}), }),

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

@ -3,6 +3,7 @@
*/ */
import type { RequestClientOptions } from '@vben/request'; import type { RequestClientOptions } from '@vben/request';
import { ResultFieldEnum } from '@vben/constants';
import { useAppConfig } from '@vben/hooks'; import { useAppConfig } from '@vben/hooks';
import { preferences } from '@vben/preferences'; import { preferences } from '@vben/preferences';
import { import {
@ -89,10 +90,10 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) {
client.addResponseInterceptor( client.addResponseInterceptor(
errorMessageResponseInterceptor((msg: string, error) => { errorMessageResponseInterceptor((msg: string, error) => {
// 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
// 当前mock接口返回的错误字段是 error 或者 message // 默认从统一响应约定的 message 字段中提取错误信息,如后端使用其他字段(如 error、msg),可在此自定义
const responseData = error?.response?.data ?? {}; const responseData = error?.response?.data ?? {};
const errorMessage = const errorMessage =
responseData?.error ?? responseData?.message ?? responseData?.msg ?? ''; responseData?.[ResultFieldEnum.MESSAGE] ?? responseData?.error ?? '';
// 如果没有错误信息,则会根据状态码进行提示 // 如果没有错误信息,则会根据状态码进行提示
message.error(errorMessage || msg); message.error(errorMessage || msg);
}), }),

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

@ -3,6 +3,7 @@
*/ */
import type { RequestClientOptions } from '@vben/request'; import type { RequestClientOptions } from '@vben/request';
import { ResultFieldEnum } from '@vben/constants';
import { useAppConfig } from '@vben/hooks'; import { useAppConfig } from '@vben/hooks';
import { preferences } from '@vben/preferences'; import { preferences } from '@vben/preferences';
import { import {
@ -89,10 +90,10 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) {
client.addResponseInterceptor( client.addResponseInterceptor(
errorMessageResponseInterceptor((msg: string, error) => { errorMessageResponseInterceptor((msg: string, error) => {
// 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
// 当前mock接口返回的错误字段是 error 或者 message // 默认从统一响应约定的 message 字段中提取错误信息,如后端使用其他字段(如 error、msg),可在此自定义
const responseData = error?.response?.data ?? {}; const responseData = error?.response?.data ?? {};
const errorMessage = const errorMessage =
responseData?.error ?? responseData?.message ?? responseData?.msg ?? ''; responseData?.[ResultFieldEnum.MESSAGE] ?? responseData?.error ?? '';
// 如果没有错误信息,则会根据状态码进行提示 // 如果没有错误信息,则会根据状态码进行提示
ElMessage.error(errorMessage || msg); ElMessage.error(errorMessage || msg);
}), }),

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

@ -3,6 +3,7 @@
*/ */
import type { RequestClientOptions } from '@vben/request'; import type { RequestClientOptions } from '@vben/request';
import { ResultFieldEnum } from '@vben/constants';
import { useAppConfig } from '@vben/hooks'; import { useAppConfig } from '@vben/hooks';
import { preferences } from '@vben/preferences'; import { preferences } from '@vben/preferences';
import { import {
@ -88,10 +89,10 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) {
client.addResponseInterceptor( client.addResponseInterceptor(
errorMessageResponseInterceptor((msg: string, error) => { errorMessageResponseInterceptor((msg: string, error) => {
// 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
// 当前mock接口返回的错误字段是 error 或者 message // 默认从统一响应约定的 message 字段中提取错误信息,如后端使用其他字段(如 error、msg),可在此自定义
const responseData = error?.response?.data ?? {}; const responseData = error?.response?.data ?? {};
const errorMessage = const errorMessage =
responseData?.error ?? responseData?.message ?? responseData?.msg ?? ''; responseData?.[ResultFieldEnum.MESSAGE] ?? responseData?.error ?? '';
// 如果没有错误信息,则会根据状态码进行提示 // 如果没有错误信息,则会根据状态码进行提示
message.error(errorMessage || msg); message.error(errorMessage || msg);
}), }),

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

@ -3,6 +3,7 @@
*/ */
import type { RequestClientOptions } from '@vben/request'; import type { RequestClientOptions } from '@vben/request';
import { ResultFieldEnum } from '@vben/constants';
import { useAppConfig } from '@vben/hooks'; import { useAppConfig } from '@vben/hooks';
import { preferences } from '@vben/preferences'; import { preferences } from '@vben/preferences';
import { import {
@ -88,10 +89,10 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) {
client.addResponseInterceptor( client.addResponseInterceptor(
errorMessageResponseInterceptor((msg: string, error) => { errorMessageResponseInterceptor((msg: string, error) => {
// 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
// 当前mock接口返回的错误字段是 error 或者 message // 默认从统一响应约定的 message 字段中提取错误信息,如后端使用其他字段(如 error、msg),可在此自定义
const responseData = error?.response?.data ?? {}; const responseData = error?.response?.data ?? {};
const errorMessage = const errorMessage =
responseData?.error ?? responseData?.message ?? responseData?.msg ?? ''; responseData?.[ResultFieldEnum.MESSAGE] ?? responseData?.error ?? '';
// 如果没有错误信息,则会根据状态码进行提示 // 如果没有错误信息,则会根据状态码进行提示
message.error(errorMessage || msg); message.error(errorMessage || msg);
}), }),

5
docs/src/en/guide/essentials/server.md

@ -167,6 +167,7 @@ The `src/api/request.ts` within the application can be configured according to t
*/ */
import type { HttpResponse } from '@vben/request'; import type { HttpResponse } from '@vben/request';
import { ResultFieldEnum } from '@vben/constants';
import { useAppConfig } from '@vben/hooks'; import { useAppConfig } from '@vben/hooks';
import { preferences } from '@vben/preferences'; import { preferences } from '@vben/preferences';
import { import {
@ -259,10 +260,10 @@ function createRequestClient(baseURL: string) {
client.addResponseInterceptor( client.addResponseInterceptor(
errorMessageResponseInterceptor((msg: string, error) => { errorMessageResponseInterceptor((msg: string, error) => {
// 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
// 当前mock接口返回的错误字段是 error 或者 message // 默认从统一响应约定的 message 字段中提取错误信息,如后端使用其他字段(如 error、msg),可在此自定义
const responseData = error?.response?.data ?? {}; const responseData = error?.response?.data ?? {};
const errorMessage = const errorMessage =
responseData?.error ?? responseData?.message ?? responseData?.msg ?? ''; responseData?.[ResultFieldEnum.MESSAGE] ?? responseData?.error ?? '';
// 如果没有错误信息,则会根据状态码进行提示 // 如果没有错误信息,则会根据状态码进行提示
message.error(errorMessage || msg); message.error(errorMessage || msg);
}), }),

5
docs/src/guide/essentials/server.md

@ -197,6 +197,7 @@ export async function deleteUserApi(userId: number) {
*/ */
import type { HttpResponse } from '@vben/request'; import type { HttpResponse } from '@vben/request';
import { ResultFieldEnum } from '@vben/constants';
import { useAppConfig } from '@vben/hooks'; import { useAppConfig } from '@vben/hooks';
import { preferences } from '@vben/preferences'; import { preferences } from '@vben/preferences';
import { import {
@ -290,10 +291,10 @@ function createRequestClient(baseURL: string) {
client.addResponseInterceptor( client.addResponseInterceptor(
errorMessageResponseInterceptor((msg: string, error) => { errorMessageResponseInterceptor((msg: string, error) => {
// 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
// 当前mock接口返回的错误字段是 error 或者 message // 默认从统一响应约定的 message 字段中提取错误信息,如后端使用其他字段(如 error、msg),可在此自定义
const responseData = error?.response?.data ?? {}; const responseData = error?.response?.data ?? {};
const errorMessage = const errorMessage =
responseData?.error ?? responseData?.message ?? responseData?.msg ?? ''; responseData?.[ResultFieldEnum.MESSAGE] ?? responseData?.error ?? '';
// 如果没有错误信息,则会根据状态码进行提示 // 如果没有错误信息,则会根据状态码进行提示
message.error(errorMessage || msg); message.error(errorMessage || msg);
}), }),

7
packages/constants/src/http.enum.ts

@ -38,6 +38,13 @@ export enum HttpResultEnum {
WARN = 601, WARN = 601,
} }
/** 返回结果字段 */
export enum ResultFieldEnum {
CODE = 'code',
DATA = 'data',
MESSAGE = 'message',
}
/** /**
* @description: request method * @description: request method
*/ */

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

@ -1,15 +1,18 @@
import type { RequestClient } from './request-client'; import type { RequestClient } from './request-client';
import type { MakeErrorMessageFn, ResponseInterceptorConfig } from './types'; import type { MakeErrorMessageFn, ResponseInterceptorConfig } from './types';
import { HttpResultEnum } from '@vben/constants'; import { HttpResultEnum, ResultFieldEnum } from '@vben/constants';
import { $t } from '@vben/locales'; import { $t } from '@vben/locales';
import { isFunction } from '@vben/utils'; import { isFunction } from '@vben/utils';
import axios from 'axios'; import axios from 'axios';
/**
* 默认的响应数据格式化拦截器,根据业务状态码判断请求是否成功,并提取需要返回的数据
*/
export const defaultResponseInterceptor = ({ export const defaultResponseInterceptor = ({
codeField = 'code', codeField = ResultFieldEnum.CODE,
dataField = 'data', dataField = ResultFieldEnum.DATA,
successCode = HttpResultEnum.SUCCESS, successCode = HttpResultEnum.SUCCESS,
}: { }: {
/** 响应数据中代表访问结果的字段名 */ /** 响应数据中代表访问结果的字段名 */
@ -19,16 +22,6 @@ export const defaultResponseInterceptor = ({
/** 当codeField所指定的字段值与successCode相同时,代表接口访问成功。如果提供一个函数,则返回true代表接口访问成功 */ /** 当codeField所指定的字段值与successCode相同时,代表接口访问成功。如果提供一个函数,则返回true代表接口访问成功 */
successCode?: ((code: any) => boolean) | number | string; successCode?: ((code: any) => boolean) | number | string;
}): ResponseInterceptorConfig => { }): ResponseInterceptorConfig => {
// 初始化字段
if (!codeField) {
codeField = 'code';
}
if (!dataField) {
dataField = 'data';
}
if (!successCode) {
successCode = HttpResultEnum.SUCCESS;
}
return { return {
fulfilled: (response) => { fulfilled: (response) => {
const { config, data: responseData, status } = response; const { config, data: responseData, status } = response;
@ -55,24 +48,35 @@ export const defaultResponseInterceptor = ({
}; };
}; };
/**
* 认证拦截器,处理401未授权错误:优先尝试刷新token并重发请求,失败则触发重新认证
*/
export const authenticateResponseInterceptor = ({ export const authenticateResponseInterceptor = ({
client, client,
doReAuthenticate, doReAuthenticate,
doRefreshToken, doRefreshToken,
enableRefreshToken, enableRefreshToken,
formatToken, formatToken,
codeField = ResultFieldEnum.CODE,
}: { }: {
/** 请求客户端实例,用于刷新token后重新发起请求 */
client: RequestClient; client: RequestClient;
/** 重新认证逻辑(token失效且无法刷新时触发,如跳转登录页) */
doReAuthenticate: () => Promise<void>; doReAuthenticate: () => Promise<void>;
/** 刷新token的逻辑 */
doRefreshToken: () => Promise<string>; doRefreshToken: () => Promise<string>;
/** 是否启用刷新token功能 */
enableRefreshToken: boolean; enableRefreshToken: boolean;
/** token格式化方法 */
formatToken: (token: string) => null | string; formatToken: (token: string) => null | string;
/** 响应数据中代表访问结果的字段名 */
codeField?: string;
}): ResponseInterceptorConfig => { }): ResponseInterceptorConfig => {
return { return {
rejected: async (error) => { rejected: async (error) => {
const { config, response } = error; const { config, response } = error;
// 如果不是 401 错误,直接抛出异常 // 如果不是 401 错误,直接抛出异常
const status = response?.data?.code || response?.status; const status = response?.data?.[codeField] || response?.status;
if (status !== HttpResultEnum.UNAUTHORIZED) { if (status !== HttpResultEnum.UNAUTHORIZED) {
throw error; throw error;
} }
@ -121,8 +125,14 @@ export const authenticateResponseInterceptor = ({
}; };
}; };
/**
* 通用的错误消息提示拦截器,根据响应状态码提示对应的错误信息
* @param makeErrorMessage - 统一的错误消息提示方法
* @param codeField - 响应数据中代表访问结果的字段名,可选
*/
export const errorMessageResponseInterceptor = ( export const errorMessageResponseInterceptor = (
makeErrorMessage?: MakeErrorMessageFn, makeErrorMessage?: MakeErrorMessageFn,
codeField: string = ResultFieldEnum.CODE,
): ResponseInterceptorConfig => { ): ResponseInterceptorConfig => {
return { return {
rejected: (error: any) => { rejected: (error: any) => {
@ -143,7 +153,8 @@ export const errorMessageResponseInterceptor = (
} }
let errorMessage: string; let errorMessage: string;
const status = error?.response?.data?.code || error?.response?.status; const status =
error?.response?.data?.[codeField] || error?.response?.status;
switch (status) { switch (status) {
case HttpResultEnum.REQUEST_TIMEOUT: { case HttpResultEnum.REQUEST_TIMEOUT: {

5
playground/src/api/request.ts

@ -3,6 +3,7 @@
*/ */
import type { AxiosResponseHeaders, RequestClientOptions } from '@vben/request'; import type { AxiosResponseHeaders, RequestClientOptions } from '@vben/request';
import { ResultFieldEnum } from '@vben/constants';
import { useAppConfig } from '@vben/hooks'; import { useAppConfig } from '@vben/hooks';
import { preferences } from '@vben/preferences'; import { preferences } from '@vben/preferences';
import { import {
@ -103,10 +104,10 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) {
client.addResponseInterceptor( client.addResponseInterceptor(
errorMessageResponseInterceptor((msg: string, error) => { errorMessageResponseInterceptor((msg: string, error) => {
// 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
// 当前mock接口返回的错误字段是 error 或者 message // 默认从统一响应约定的 message 字段中提取错误信息,如后端使用其他字段(如 error、msg),可在此自定义
const responseData = error?.response?.data ?? {}; const responseData = error?.response?.data ?? {};
const errorMessage = const errorMessage =
responseData?.error ?? responseData?.message ?? responseData?.msg ?? ''; responseData?.[ResultFieldEnum.MESSAGE] ?? responseData?.error ?? '';
// 如果没有错误信息,则会根据状态码进行提示 // 如果没有错误信息,则会根据状态码进行提示
message.error(errorMessage || msg); message.error(errorMessage || msg);
}), }),

Loading…
Cancel
Save