diff --git a/apps/web-antd/src/api/request.ts b/apps/web-antd/src/api/request.ts index 987aed55c..f6e1b083d 100644 --- a/apps/web-antd/src/api/request.ts +++ b/apps/web-antd/src/api/request.ts @@ -3,6 +3,7 @@ */ import type { RequestClientOptions } from '@vben/request'; +import { ResultFieldEnum } from '@vben/constants'; import { useAppConfig } from '@vben/hooks'; import { preferences } from '@vben/preferences'; import { @@ -89,10 +90,10 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) { client.addResponseInterceptor( errorMessageResponseInterceptor((msg: string, error) => { // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg - // 当前mock接口返回的错误字段是 error 或者 message + // 默认从统一响应约定的 message 字段中提取错误信息,如后端使用其他字段(如 error、msg),可在此自定义 const responseData = error?.response?.data ?? {}; const errorMessage = - responseData?.error ?? responseData?.message ?? responseData?.msg ?? ''; + responseData?.[ResultFieldEnum.MESSAGE] ?? responseData?.error ?? ''; // 如果没有错误信息,则会根据状态码进行提示 message.error(errorMessage || msg); }), diff --git a/apps/web-antdv-next/src/api/request.ts b/apps/web-antdv-next/src/api/request.ts index 4b3599ce6..6dc99cb11 100644 --- a/apps/web-antdv-next/src/api/request.ts +++ b/apps/web-antdv-next/src/api/request.ts @@ -3,6 +3,7 @@ */ import type { RequestClientOptions } from '@vben/request'; +import { ResultFieldEnum } from '@vben/constants'; import { useAppConfig } from '@vben/hooks'; import { preferences } from '@vben/preferences'; import { @@ -89,10 +90,10 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) { client.addResponseInterceptor( errorMessageResponseInterceptor((msg: string, error) => { // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg - // 当前mock接口返回的错误字段是 error 或者 message + // 默认从统一响应约定的 message 字段中提取错误信息,如后端使用其他字段(如 error、msg),可在此自定义 const responseData = error?.response?.data ?? {}; const errorMessage = - responseData?.error ?? responseData?.message ?? responseData?.msg ?? ''; + responseData?.[ResultFieldEnum.MESSAGE] ?? responseData?.error ?? ''; // 如果没有错误信息,则会根据状态码进行提示 message.error(errorMessage || msg); }), diff --git a/apps/web-ele/src/api/request.ts b/apps/web-ele/src/api/request.ts index 23760fe4f..a06d9baba 100644 --- a/apps/web-ele/src/api/request.ts +++ b/apps/web-ele/src/api/request.ts @@ -3,6 +3,7 @@ */ import type { RequestClientOptions } from '@vben/request'; +import { ResultFieldEnum } from '@vben/constants'; import { useAppConfig } from '@vben/hooks'; import { preferences } from '@vben/preferences'; import { @@ -89,10 +90,10 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) { client.addResponseInterceptor( errorMessageResponseInterceptor((msg: string, error) => { // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg - // 当前mock接口返回的错误字段是 error 或者 message + // 默认从统一响应约定的 message 字段中提取错误信息,如后端使用其他字段(如 error、msg),可在此自定义 const responseData = error?.response?.data ?? {}; const errorMessage = - responseData?.error ?? responseData?.message ?? responseData?.msg ?? ''; + responseData?.[ResultFieldEnum.MESSAGE] ?? responseData?.error ?? ''; // 如果没有错误信息,则会根据状态码进行提示 ElMessage.error(errorMessage || msg); }), diff --git a/apps/web-naive/src/api/request.ts b/apps/web-naive/src/api/request.ts index 5a154f224..328a34411 100644 --- a/apps/web-naive/src/api/request.ts +++ b/apps/web-naive/src/api/request.ts @@ -3,6 +3,7 @@ */ import type { RequestClientOptions } from '@vben/request'; +import { ResultFieldEnum } from '@vben/constants'; import { useAppConfig } from '@vben/hooks'; import { preferences } from '@vben/preferences'; import { @@ -88,10 +89,10 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) { client.addResponseInterceptor( errorMessageResponseInterceptor((msg: string, error) => { // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg - // 当前mock接口返回的错误字段是 error 或者 message + // 默认从统一响应约定的 message 字段中提取错误信息,如后端使用其他字段(如 error、msg),可在此自定义 const responseData = error?.response?.data ?? {}; const errorMessage = - responseData?.error ?? responseData?.message ?? responseData?.msg ?? ''; + responseData?.[ResultFieldEnum.MESSAGE] ?? responseData?.error ?? ''; // 如果没有错误信息,则会根据状态码进行提示 message.error(errorMessage || msg); }), diff --git a/apps/web-tdesign/src/api/request.ts b/apps/web-tdesign/src/api/request.ts index 473e849b6..988f7c927 100644 --- a/apps/web-tdesign/src/api/request.ts +++ b/apps/web-tdesign/src/api/request.ts @@ -3,6 +3,7 @@ */ import type { RequestClientOptions } from '@vben/request'; +import { ResultFieldEnum } from '@vben/constants'; import { useAppConfig } from '@vben/hooks'; import { preferences } from '@vben/preferences'; import { @@ -88,10 +89,10 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) { client.addResponseInterceptor( errorMessageResponseInterceptor((msg: string, error) => { // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg - // 当前mock接口返回的错误字段是 error 或者 message + // 默认从统一响应约定的 message 字段中提取错误信息,如后端使用其他字段(如 error、msg),可在此自定义 const responseData = error?.response?.data ?? {}; const errorMessage = - responseData?.error ?? responseData?.message ?? responseData?.msg ?? ''; + responseData?.[ResultFieldEnum.MESSAGE] ?? responseData?.error ?? ''; // 如果没有错误信息,则会根据状态码进行提示 message.error(errorMessage || msg); }), diff --git a/docs/src/en/guide/essentials/server.md b/docs/src/en/guide/essentials/server.md index ae68af36c..42eb51c27 100644 --- a/docs/src/en/guide/essentials/server.md +++ b/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 { ResultFieldEnum } from '@vben/constants'; import { useAppConfig } from '@vben/hooks'; import { preferences } from '@vben/preferences'; import { @@ -259,10 +260,10 @@ function createRequestClient(baseURL: string) { client.addResponseInterceptor( errorMessageResponseInterceptor((msg: string, error) => { // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg - // 当前mock接口返回的错误字段是 error 或者 message + // 默认从统一响应约定的 message 字段中提取错误信息,如后端使用其他字段(如 error、msg),可在此自定义 const responseData = error?.response?.data ?? {}; const errorMessage = - responseData?.error ?? responseData?.message ?? responseData?.msg ?? ''; + responseData?.[ResultFieldEnum.MESSAGE] ?? responseData?.error ?? ''; // 如果没有错误信息,则会根据状态码进行提示 message.error(errorMessage || msg); }), diff --git a/docs/src/guide/essentials/server.md b/docs/src/guide/essentials/server.md index 253419473..82b2ba0f5 100644 --- a/docs/src/guide/essentials/server.md +++ b/docs/src/guide/essentials/server.md @@ -197,6 +197,7 @@ export async function deleteUserApi(userId: number) { */ import type { HttpResponse } from '@vben/request'; +import { ResultFieldEnum } from '@vben/constants'; import { useAppConfig } from '@vben/hooks'; import { preferences } from '@vben/preferences'; import { @@ -290,10 +291,10 @@ function createRequestClient(baseURL: string) { client.addResponseInterceptor( errorMessageResponseInterceptor((msg: string, error) => { // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg - // 当前mock接口返回的错误字段是 error 或者 message + // 默认从统一响应约定的 message 字段中提取错误信息,如后端使用其他字段(如 error、msg),可在此自定义 const responseData = error?.response?.data ?? {}; const errorMessage = - responseData?.error ?? responseData?.message ?? responseData?.msg ?? ''; + responseData?.[ResultFieldEnum.MESSAGE] ?? responseData?.error ?? ''; // 如果没有错误信息,则会根据状态码进行提示 message.error(errorMessage || msg); }), diff --git a/packages/constants/src/http.enum.ts b/packages/constants/src/http.enum.ts index ccee40d37..e1bf4dff7 100644 --- a/packages/constants/src/http.enum.ts +++ b/packages/constants/src/http.enum.ts @@ -38,6 +38,13 @@ export enum HttpResultEnum { WARN = 601, } +/** 返回结果字段 */ +export enum ResultFieldEnum { + CODE = 'code', + DATA = 'data', + MESSAGE = 'message', +} + /** * @description: request method */ diff --git a/packages/effects/request/src/request-client/preset-interceptors.ts b/packages/effects/request/src/request-client/preset-interceptors.ts index 356a6dcd3..bea5f47b4 100644 --- a/packages/effects/request/src/request-client/preset-interceptors.ts +++ b/packages/effects/request/src/request-client/preset-interceptors.ts @@ -1,15 +1,18 @@ import type { RequestClient } from './request-client'; import type { MakeErrorMessageFn, ResponseInterceptorConfig } from './types'; -import { HttpResultEnum } from '@vben/constants'; +import { HttpResultEnum, ResultFieldEnum } from '@vben/constants'; import { $t } from '@vben/locales'; import { isFunction } from '@vben/utils'; import axios from 'axios'; +/** + * 默认的响应数据格式化拦截器,根据业务状态码判断请求是否成功,并提取需要返回的数据 + */ export const defaultResponseInterceptor = ({ - codeField = 'code', - dataField = 'data', + codeField = ResultFieldEnum.CODE, + dataField = ResultFieldEnum.DATA, successCode = HttpResultEnum.SUCCESS, }: { /** 响应数据中代表访问结果的字段名 */ @@ -19,16 +22,6 @@ export const defaultResponseInterceptor = ({ /** 当codeField所指定的字段值与successCode相同时,代表接口访问成功。如果提供一个函数,则返回true代表接口访问成功 */ successCode?: ((code: any) => boolean) | number | string; }): ResponseInterceptorConfig => { - // 初始化字段 - if (!codeField) { - codeField = 'code'; - } - if (!dataField) { - dataField = 'data'; - } - if (!successCode) { - successCode = HttpResultEnum.SUCCESS; - } return { fulfilled: (response) => { const { config, data: responseData, status } = response; @@ -55,24 +48,35 @@ export const defaultResponseInterceptor = ({ }; }; +/** + * 认证拦截器,处理401未授权错误:优先尝试刷新token并重发请求,失败则触发重新认证 + */ export const authenticateResponseInterceptor = ({ client, doReAuthenticate, doRefreshToken, enableRefreshToken, formatToken, + codeField = ResultFieldEnum.CODE, }: { + /** 请求客户端实例,用于刷新token后重新发起请求 */ client: RequestClient; + /** 重新认证逻辑(token失效且无法刷新时触发,如跳转登录页) */ doReAuthenticate: () => Promise; + /** 刷新token的逻辑 */ doRefreshToken: () => Promise; + /** 是否启用刷新token功能 */ enableRefreshToken: boolean; + /** token格式化方法 */ formatToken: (token: string) => null | string; + /** 响应数据中代表访问结果的字段名 */ + codeField?: string; }): ResponseInterceptorConfig => { return { rejected: async (error) => { const { config, response } = error; // 如果不是 401 错误,直接抛出异常 - const status = response?.data?.code || response?.status; + const status = response?.data?.[codeField] || response?.status; if (status !== HttpResultEnum.UNAUTHORIZED) { throw error; } @@ -121,8 +125,14 @@ export const authenticateResponseInterceptor = ({ }; }; +/** + * 通用的错误消息提示拦截器,根据响应状态码提示对应的错误信息 + * @param makeErrorMessage - 统一的错误消息提示方法 + * @param codeField - 响应数据中代表访问结果的字段名,可选 + */ export const errorMessageResponseInterceptor = ( makeErrorMessage?: MakeErrorMessageFn, + codeField: string = ResultFieldEnum.CODE, ): ResponseInterceptorConfig => { return { rejected: (error: any) => { @@ -143,7 +153,8 @@ export const errorMessageResponseInterceptor = ( } let errorMessage: string; - const status = error?.response?.data?.code || error?.response?.status; + const status = + error?.response?.data?.[codeField] || error?.response?.status; switch (status) { case HttpResultEnum.REQUEST_TIMEOUT: { diff --git a/playground/src/api/request.ts b/playground/src/api/request.ts index 3038c6ecc..9a6dd9ba3 100644 --- a/playground/src/api/request.ts +++ b/playground/src/api/request.ts @@ -3,6 +3,7 @@ */ import type { AxiosResponseHeaders, RequestClientOptions } from '@vben/request'; +import { ResultFieldEnum } from '@vben/constants'; import { useAppConfig } from '@vben/hooks'; import { preferences } from '@vben/preferences'; import { @@ -103,10 +104,10 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) { client.addResponseInterceptor( errorMessageResponseInterceptor((msg: string, error) => { // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg - // 当前mock接口返回的错误字段是 error 或者 message + // 默认从统一响应约定的 message 字段中提取错误信息,如后端使用其他字段(如 error、msg),可在此自定义 const responseData = error?.response?.data ?? {}; const errorMessage = - responseData?.error ?? responseData?.message ?? responseData?.msg ?? ''; + responseData?.[ResultFieldEnum.MESSAGE] ?? responseData?.error ?? ''; // 如果没有错误信息,则会根据状态码进行提示 message.error(errorMessage || msg); }),