Selaa lähdekoodia

perf: request support to set how to return response (#5436)

* feat: request support to set how to return response

* docs: typo

* fix: test unit

* test: add request responseReturn test
Netfan 3 kuukautta sitten
vanhempi
commit
5611f6c7f5

+ 6 - 17
apps/web-antd/src/api/request.ts

@@ -1,7 +1,7 @@
 /**
  * 该文件可自行根据业务逻辑进行调整
  */
-import type { HttpResponse } from '@vben/request';
+import type { RequestClientOptions } from '@vben/request';
 
 import { useAppConfig } from '@vben/hooks';
 import { preferences } from '@vben/preferences';
@@ -20,8 +20,9 @@ import { refreshTokenApi } from './core';
 
 const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
 
-function createRequestClient(baseURL: string) {
+function createRequestClient(baseURL: string, options?: RequestClientOptions) {
   const client = new RequestClient({
+    ...options,
     baseURL,
   });
 
@@ -69,20 +70,6 @@ function createRequestClient(baseURL: string) {
     },
   });
 
-  // response数据解构
-  client.addResponseInterceptor<HttpResponse>({
-    fulfilled: (response) => {
-      const { data: responseData, status } = response;
-
-      const { code, data } = responseData;
-      if (status >= 200 && status < 400 && code === 0) {
-        return data;
-      }
-
-      throw Object.assign({}, response, { response });
-    },
-  });
-
   // token过期的处理
   client.addResponseInterceptor(
     authenticateResponseInterceptor({
@@ -109,6 +96,8 @@ function createRequestClient(baseURL: string) {
   return client;
 }
 
-export const requestClient = createRequestClient(apiURL);
+export const requestClient = createRequestClient(apiURL, {
+  responseReturn: 'data',
+});
 
 export const baseRequestClient = new RequestClient({ baseURL: apiURL });

+ 6 - 16
apps/web-ele/src/api/request.ts

@@ -1,7 +1,7 @@
 /**
  * 该文件可自行根据业务逻辑进行调整
  */
-import type { HttpResponse } from '@vben/request';
+import type { RequestClientOptions } from '@vben/request';
 
 import { useAppConfig } from '@vben/hooks';
 import { preferences } from '@vben/preferences';
@@ -20,8 +20,9 @@ import { refreshTokenApi } from './core';
 
 const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
 
-function createRequestClient(baseURL: string) {
+function createRequestClient(baseURL: string, options?: RequestClientOptions) {
   const client = new RequestClient({
+    ...options,
     baseURL,
   });
 
@@ -69,19 +70,6 @@ function createRequestClient(baseURL: string) {
     },
   });
 
-  // response数据解构
-  client.addResponseInterceptor<HttpResponse>({
-    fulfilled: (response) => {
-      const { data: responseData, status } = response;
-
-      const { code, data } = responseData;
-      if (status >= 200 && status < 400 && code === 0) {
-        return data;
-      }
-      throw Object.assign({}, response, { response });
-    },
-  });
-
   // token过期的处理
   client.addResponseInterceptor(
     authenticateResponseInterceptor({
@@ -108,6 +96,8 @@ function createRequestClient(baseURL: string) {
   return client;
 }
 
-export const requestClient = createRequestClient(apiURL);
+export const requestClient = createRequestClient(apiURL, {
+  responseReturn: 'data',
+});
 
 export const baseRequestClient = new RequestClient({ baseURL: apiURL });

+ 6 - 16
apps/web-naive/src/api/request.ts

@@ -1,7 +1,7 @@
 /**
  * 该文件可自行根据业务逻辑进行调整
  */
-import type { HttpResponse } from '@vben/request';
+import type { RequestClientOptions } from '@vben/request';
 
 import { useAppConfig } from '@vben/hooks';
 import { preferences } from '@vben/preferences';
@@ -19,8 +19,9 @@ import { refreshTokenApi } from './core';
 
 const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
 
-function createRequestClient(baseURL: string) {
+function createRequestClient(baseURL: string, options?: RequestClientOptions) {
   const client = new RequestClient({
+    ...options,
     baseURL,
   });
 
@@ -68,19 +69,6 @@ function createRequestClient(baseURL: string) {
     },
   });
 
-  // response数据解构
-  client.addResponseInterceptor<HttpResponse>({
-    fulfilled: (response) => {
-      const { data: responseData, status } = response;
-
-      const { code, data } = responseData;
-      if (status >= 200 && status < 400 && code === 0) {
-        return data;
-      }
-      throw Object.assign({}, response, { response });
-    },
-  });
-
   // token过期的处理
   client.addResponseInterceptor(
     authenticateResponseInterceptor({
@@ -107,6 +95,8 @@ function createRequestClient(baseURL: string) {
   return client;
 }
 
-export const requestClient = createRequestClient(apiURL);
+export const requestClient = createRequestClient(apiURL, {
+  responseReturn: 'data',
+});
 
 export const baseRequestClient = new RequestClient({ baseURL: apiURL });

+ 23 - 0
packages/effects/request/src/request-client/request-client.test.ts

@@ -48,6 +48,29 @@ describe('requestClient', () => {
     expect(response.data).toEqual(mockData);
   });
 
+  it('should return different response types', async () => {
+    const mockData = { code: 0, msg: 'ok', data: 'response' };
+    mock.onGet('/test/diff').reply(200, mockData);
+
+    const responseRaw = await requestClient.get('/test/diff', {
+      responseReturn: 'raw',
+    });
+    expect(responseRaw.status).toBe(200);
+    expect(responseRaw.data).toEqual(mockData);
+
+    const responseBody = await requestClient.get('/test/diff', {
+      responseReturn: 'body',
+    });
+    expect(responseBody.code).toEqual(mockData.code);
+    expect(responseBody.msg).toEqual(mockData.msg);
+    expect(responseBody.data).toEqual(mockData.data);
+
+    const responseData = await requestClient.get('/test/diff', {
+      responseReturn: 'data',
+    });
+    expect(responseData).toEqual(mockData.data);
+  });
+
   it('should handle network errors', async () => {
     mock.onGet('/test/error').networkError();
     try {

+ 37 - 13
packages/effects/request/src/request-client/request-client.ts

@@ -1,11 +1,10 @@
-import type {
-  AxiosInstance,
-  AxiosRequestConfig,
-  AxiosResponse,
-  CreateAxiosDefaults,
-} from 'axios';
+import type { AxiosInstance, AxiosResponse } from 'axios';
 
-import type { RequestClientOptions } from './types';
+import type {
+  HttpResponse,
+  RequestClientConfig,
+  RequestClientOptions,
+} from './types';
 
 import { bindMethods, merge } from '@vben/utils';
 
@@ -34,10 +33,11 @@ class RequestClient {
    */
   constructor(options: RequestClientOptions = {}) {
     // 合并默认配置和传入的配置
-    const defaultConfig: CreateAxiosDefaults = {
+    const defaultConfig: RequestClientOptions = {
       headers: {
         'Content-Type': 'application/json;charset=utf-8',
       },
+      responseReturn: 'raw',
       // 默认超时时间
       timeout: 10_000,
     };
@@ -54,6 +54,24 @@ class RequestClient {
     this.addResponseInterceptor =
       interceptorManager.addResponseInterceptor.bind(interceptorManager);
 
+    // 添加基础的响应处理,根据设置决定返回响应的哪一部分
+    this.addResponseInterceptor<HttpResponse>({
+      fulfilled: (response) => {
+        const { config, data: responseData, status } = response;
+
+        if (config.responseReturn === 'raw') {
+          return response;
+        }
+
+        const { code, data } = responseData;
+
+        if (status >= 200 && status < 400 && code === 0) {
+          return config.responseReturn === 'body' ? responseData : data;
+        }
+        throw Object.assign({}, response, { response });
+      },
+    });
+
     // 实例化文件上传器
     const fileUploader = new FileUploader(this);
     this.upload = fileUploader.upload.bind(fileUploader);
@@ -65,14 +83,17 @@ class RequestClient {
   /**
    * DELETE请求方法
    */
-  public delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
+  public delete<T = any>(
+    url: string,
+    config?: RequestClientConfig,
+  ): Promise<T> {
     return this.request<T>(url, { ...config, method: 'DELETE' });
   }
 
   /**
    * GET请求方法
    */
-  public get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
+  public get<T = any>(url: string, config?: RequestClientConfig): Promise<T> {
     return this.request<T>(url, { ...config, method: 'GET' });
   }
 
@@ -82,7 +103,7 @@ class RequestClient {
   public post<T = any>(
     url: string,
     data?: any,
-    config?: AxiosRequestConfig,
+    config?: RequestClientConfig,
   ): Promise<T> {
     return this.request<T>(url, { ...config, data, method: 'POST' });
   }
@@ -93,7 +114,7 @@ class RequestClient {
   public put<T = any>(
     url: string,
     data?: any,
-    config?: AxiosRequestConfig,
+    config?: RequestClientConfig,
   ): Promise<T> {
     return this.request<T>(url, { ...config, data, method: 'PUT' });
   }
@@ -101,7 +122,10 @@ class RequestClient {
   /**
    * 通用的请求方法
    */
-  public async request<T>(url: string, config: AxiosRequestConfig): Promise<T> {
+  public async request<T>(
+    url: string,
+    config: RequestClientConfig,
+  ): Promise<T> {
     try {
       const response: AxiosResponse<T> = await this.instance({
         url,

+ 21 - 7
packages/effects/request/src/request-client/types.ts

@@ -1,10 +1,23 @@
 import type {
+  AxiosRequestConfig,
   AxiosResponse,
   CreateAxiosDefaults,
   InternalAxiosRequestConfig,
 } from 'axios';
 
-type RequestResponse<T = any> = AxiosResponse<T>;
+type ExtendOptions = {
+  /** 响应数据的返回方式。
+   * raw: 原始的AxiosResponse,包括headers、status等。
+   * body: 返回响应数据的BODY部分。
+   * data: 解构响应的BODY数据,只返回其中的data节点数据。
+   */
+  responseReturn?: 'body' | 'data' | 'raw';
+};
+type RequestClientConfig<T = any> = AxiosRequestConfig<T> & ExtendOptions;
+
+type RequestResponse<T = any> = AxiosResponse<T> & {
+  config: RequestClientConfig<T>;
+};
 
 type RequestContentType =
   | 'application/json;charset=utf-8'
@@ -12,21 +25,21 @@ type RequestContentType =
   | 'application/x-www-form-urlencoded;charset=utf-8'
   | 'multipart/form-data;charset=utf-8';
 
-type RequestClientOptions = CreateAxiosDefaults;
+type RequestClientOptions = CreateAxiosDefaults & ExtendOptions;
 
 interface RequestInterceptorConfig {
   fulfilled?: (
-    config: InternalAxiosRequestConfig,
+    config: ExtendOptions & InternalAxiosRequestConfig,
   ) =>
-    | InternalAxiosRequestConfig<any>
-    | Promise<InternalAxiosRequestConfig<any>>;
+    | (ExtendOptions & InternalAxiosRequestConfig<any>)
+    | Promise<ExtendOptions & InternalAxiosRequestConfig<any>>;
   rejected?: (error: any) => any;
 }
 
 interface ResponseInterceptorConfig<T = any> {
   fulfilled?: (
-    response: AxiosResponse<T>,
-  ) => AxiosResponse | Promise<AxiosResponse>;
+    response: RequestResponse<T>,
+  ) => Promise<RequestResponse> | RequestResponse;
   rejected?: (error: any) => any;
 }
 
@@ -45,6 +58,7 @@ interface HttpResponse<T = any> {
 export type {
   HttpResponse,
   MakeErrorMessageFn,
+  RequestClientConfig,
   RequestClientOptions,
   RequestContentType,
   RequestInterceptorConfig,

+ 6 - 17
playground/src/api/request.ts

@@ -1,7 +1,7 @@
 /**
  * 该文件可自行根据业务逻辑进行调整
  */
-import type { HttpResponse } from '@vben/request';
+import type { RequestClientOptions } from '@vben/request';
 
 import { useAppConfig } from '@vben/hooks';
 import { preferences } from '@vben/preferences';
@@ -20,8 +20,9 @@ import { refreshTokenApi } from './core';
 
 const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
 
-function createRequestClient(baseURL: string) {
+function createRequestClient(baseURL: string, options?: RequestClientOptions) {
   const client = new RequestClient({
+    ...options,
     baseURL,
   });
 
@@ -69,20 +70,6 @@ function createRequestClient(baseURL: string) {
     },
   });
 
-  // response数据解构
-  client.addResponseInterceptor<HttpResponse>({
-    fulfilled: (response) => {
-      const { data: responseData, status } = response;
-
-      const { code, data } = responseData;
-
-      if (status >= 200 && status < 400 && code === 0) {
-        return data;
-      }
-      throw Object.assign({}, response, { response });
-    },
-  });
-
   // token过期的处理
   client.addResponseInterceptor(
     authenticateResponseInterceptor({
@@ -109,6 +96,8 @@ function createRequestClient(baseURL: string) {
   return client;
 }
 
-export const requestClient = createRequestClient(apiURL);
+export const requestClient = createRequestClient(apiURL, {
+  responseReturn: 'data',
+});
 
 export const baseRequestClient = new RequestClient({ baseURL: apiURL });