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
This commit is contained in:
parent
3f0f4d50a1
commit
5611f6c7f5
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* 该文件可自行根据业务逻辑进行调整
|
* 该文件可自行根据业务逻辑进行调整
|
||||||
*/
|
*/
|
||||||
import type { HttpResponse } from '@vben/request';
|
import type { RequestClientOptions } from '@vben/request';
|
||||||
|
|
||||||
import { useAppConfig } from '@vben/hooks';
|
import { useAppConfig } from '@vben/hooks';
|
||||||
import { preferences } from '@vben/preferences';
|
import { preferences } from '@vben/preferences';
|
||||||
@ -20,8 +20,9 @@ import { refreshTokenApi } from './core';
|
|||||||
|
|
||||||
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
|
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
|
||||||
|
|
||||||
function createRequestClient(baseURL: string) {
|
function createRequestClient(baseURL: string, options?: RequestClientOptions) {
|
||||||
const client = new RequestClient({
|
const client = new RequestClient({
|
||||||
|
...options,
|
||||||
baseURL,
|
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过期的处理
|
// token过期的处理
|
||||||
client.addResponseInterceptor(
|
client.addResponseInterceptor(
|
||||||
authenticateResponseInterceptor({
|
authenticateResponseInterceptor({
|
||||||
@ -109,6 +96,8 @@ function createRequestClient(baseURL: string) {
|
|||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const requestClient = createRequestClient(apiURL);
|
export const requestClient = createRequestClient(apiURL, {
|
||||||
|
responseReturn: 'data',
|
||||||
|
});
|
||||||
|
|
||||||
export const baseRequestClient = new RequestClient({ baseURL: apiURL });
|
export const baseRequestClient = new RequestClient({ baseURL: apiURL });
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* 该文件可自行根据业务逻辑进行调整
|
* 该文件可自行根据业务逻辑进行调整
|
||||||
*/
|
*/
|
||||||
import type { HttpResponse } from '@vben/request';
|
import type { RequestClientOptions } from '@vben/request';
|
||||||
|
|
||||||
import { useAppConfig } from '@vben/hooks';
|
import { useAppConfig } from '@vben/hooks';
|
||||||
import { preferences } from '@vben/preferences';
|
import { preferences } from '@vben/preferences';
|
||||||
@ -20,8 +20,9 @@ import { refreshTokenApi } from './core';
|
|||||||
|
|
||||||
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
|
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
|
||||||
|
|
||||||
function createRequestClient(baseURL: string) {
|
function createRequestClient(baseURL: string, options?: RequestClientOptions) {
|
||||||
const client = new RequestClient({
|
const client = new RequestClient({
|
||||||
|
...options,
|
||||||
baseURL,
|
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过期的处理
|
// token过期的处理
|
||||||
client.addResponseInterceptor(
|
client.addResponseInterceptor(
|
||||||
authenticateResponseInterceptor({
|
authenticateResponseInterceptor({
|
||||||
@ -108,6 +96,8 @@ function createRequestClient(baseURL: string) {
|
|||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const requestClient = createRequestClient(apiURL);
|
export const requestClient = createRequestClient(apiURL, {
|
||||||
|
responseReturn: 'data',
|
||||||
|
});
|
||||||
|
|
||||||
export const baseRequestClient = new RequestClient({ baseURL: apiURL });
|
export const baseRequestClient = new RequestClient({ baseURL: apiURL });
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* 该文件可自行根据业务逻辑进行调整
|
* 该文件可自行根据业务逻辑进行调整
|
||||||
*/
|
*/
|
||||||
import type { HttpResponse } from '@vben/request';
|
import type { RequestClientOptions } from '@vben/request';
|
||||||
|
|
||||||
import { useAppConfig } from '@vben/hooks';
|
import { useAppConfig } from '@vben/hooks';
|
||||||
import { preferences } from '@vben/preferences';
|
import { preferences } from '@vben/preferences';
|
||||||
@ -19,8 +19,9 @@ import { refreshTokenApi } from './core';
|
|||||||
|
|
||||||
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
|
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
|
||||||
|
|
||||||
function createRequestClient(baseURL: string) {
|
function createRequestClient(baseURL: string, options?: RequestClientOptions) {
|
||||||
const client = new RequestClient({
|
const client = new RequestClient({
|
||||||
|
...options,
|
||||||
baseURL,
|
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过期的处理
|
// token过期的处理
|
||||||
client.addResponseInterceptor(
|
client.addResponseInterceptor(
|
||||||
authenticateResponseInterceptor({
|
authenticateResponseInterceptor({
|
||||||
@ -107,6 +95,8 @@ function createRequestClient(baseURL: string) {
|
|||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const requestClient = createRequestClient(apiURL);
|
export const requestClient = createRequestClient(apiURL, {
|
||||||
|
responseReturn: 'data',
|
||||||
|
});
|
||||||
|
|
||||||
export const baseRequestClient = new RequestClient({ baseURL: apiURL });
|
export const baseRequestClient = new RequestClient({ baseURL: apiURL });
|
||||||
|
@ -48,6 +48,29 @@ describe('requestClient', () => {
|
|||||||
expect(response.data).toEqual(mockData);
|
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 () => {
|
it('should handle network errors', async () => {
|
||||||
mock.onGet('/test/error').networkError();
|
mock.onGet('/test/error').networkError();
|
||||||
try {
|
try {
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import type {
|
import type { AxiosInstance, AxiosResponse } from 'axios';
|
||||||
AxiosInstance,
|
|
||||||
AxiosRequestConfig,
|
|
||||||
AxiosResponse,
|
|
||||||
CreateAxiosDefaults,
|
|
||||||
} from 'axios';
|
|
||||||
|
|
||||||
import type { RequestClientOptions } from './types';
|
import type {
|
||||||
|
HttpResponse,
|
||||||
|
RequestClientConfig,
|
||||||
|
RequestClientOptions,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
import { bindMethods, merge } from '@vben/utils';
|
import { bindMethods, merge } from '@vben/utils';
|
||||||
|
|
||||||
@ -34,10 +33,11 @@ class RequestClient {
|
|||||||
*/
|
*/
|
||||||
constructor(options: RequestClientOptions = {}) {
|
constructor(options: RequestClientOptions = {}) {
|
||||||
// 合并默认配置和传入的配置
|
// 合并默认配置和传入的配置
|
||||||
const defaultConfig: CreateAxiosDefaults = {
|
const defaultConfig: RequestClientOptions = {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json;charset=utf-8',
|
'Content-Type': 'application/json;charset=utf-8',
|
||||||
},
|
},
|
||||||
|
responseReturn: 'raw',
|
||||||
// 默认超时时间
|
// 默认超时时间
|
||||||
timeout: 10_000,
|
timeout: 10_000,
|
||||||
};
|
};
|
||||||
@ -54,6 +54,24 @@ class RequestClient {
|
|||||||
this.addResponseInterceptor =
|
this.addResponseInterceptor =
|
||||||
interceptorManager.addResponseInterceptor.bind(interceptorManager);
|
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);
|
const fileUploader = new FileUploader(this);
|
||||||
this.upload = fileUploader.upload.bind(fileUploader);
|
this.upload = fileUploader.upload.bind(fileUploader);
|
||||||
@ -65,14 +83,17 @@ class RequestClient {
|
|||||||
/**
|
/**
|
||||||
* DELETE请求方法
|
* 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' });
|
return this.request<T>(url, { ...config, method: 'DELETE' });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET请求方法
|
* 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' });
|
return this.request<T>(url, { ...config, method: 'GET' });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +103,7 @@ class RequestClient {
|
|||||||
public post<T = any>(
|
public post<T = any>(
|
||||||
url: string,
|
url: string,
|
||||||
data?: any,
|
data?: any,
|
||||||
config?: AxiosRequestConfig,
|
config?: RequestClientConfig,
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
return this.request<T>(url, { ...config, data, method: 'POST' });
|
return this.request<T>(url, { ...config, data, method: 'POST' });
|
||||||
}
|
}
|
||||||
@ -93,7 +114,7 @@ class RequestClient {
|
|||||||
public put<T = any>(
|
public put<T = any>(
|
||||||
url: string,
|
url: string,
|
||||||
data?: any,
|
data?: any,
|
||||||
config?: AxiosRequestConfig,
|
config?: RequestClientConfig,
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
return this.request<T>(url, { ...config, data, method: 'PUT' });
|
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 {
|
try {
|
||||||
const response: AxiosResponse<T> = await this.instance({
|
const response: AxiosResponse<T> = await this.instance({
|
||||||
url,
|
url,
|
||||||
|
@ -1,10 +1,23 @@
|
|||||||
import type {
|
import type {
|
||||||
|
AxiosRequestConfig,
|
||||||
AxiosResponse,
|
AxiosResponse,
|
||||||
CreateAxiosDefaults,
|
CreateAxiosDefaults,
|
||||||
InternalAxiosRequestConfig,
|
InternalAxiosRequestConfig,
|
||||||
} from 'axios';
|
} 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 =
|
type RequestContentType =
|
||||||
| 'application/json;charset=utf-8'
|
| 'application/json;charset=utf-8'
|
||||||
@ -12,21 +25,21 @@ type RequestContentType =
|
|||||||
| 'application/x-www-form-urlencoded;charset=utf-8'
|
| 'application/x-www-form-urlencoded;charset=utf-8'
|
||||||
| 'multipart/form-data;charset=utf-8';
|
| 'multipart/form-data;charset=utf-8';
|
||||||
|
|
||||||
type RequestClientOptions = CreateAxiosDefaults;
|
type RequestClientOptions = CreateAxiosDefaults & ExtendOptions;
|
||||||
|
|
||||||
interface RequestInterceptorConfig {
|
interface RequestInterceptorConfig {
|
||||||
fulfilled?: (
|
fulfilled?: (
|
||||||
config: InternalAxiosRequestConfig,
|
config: ExtendOptions & InternalAxiosRequestConfig,
|
||||||
) =>
|
) =>
|
||||||
| InternalAxiosRequestConfig<any>
|
| (ExtendOptions & InternalAxiosRequestConfig<any>)
|
||||||
| Promise<InternalAxiosRequestConfig<any>>;
|
| Promise<ExtendOptions & InternalAxiosRequestConfig<any>>;
|
||||||
rejected?: (error: any) => any;
|
rejected?: (error: any) => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ResponseInterceptorConfig<T = any> {
|
interface ResponseInterceptorConfig<T = any> {
|
||||||
fulfilled?: (
|
fulfilled?: (
|
||||||
response: AxiosResponse<T>,
|
response: RequestResponse<T>,
|
||||||
) => AxiosResponse | Promise<AxiosResponse>;
|
) => Promise<RequestResponse> | RequestResponse;
|
||||||
rejected?: (error: any) => any;
|
rejected?: (error: any) => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,6 +58,7 @@ interface HttpResponse<T = any> {
|
|||||||
export type {
|
export type {
|
||||||
HttpResponse,
|
HttpResponse,
|
||||||
MakeErrorMessageFn,
|
MakeErrorMessageFn,
|
||||||
|
RequestClientConfig,
|
||||||
RequestClientOptions,
|
RequestClientOptions,
|
||||||
RequestContentType,
|
RequestContentType,
|
||||||
RequestInterceptorConfig,
|
RequestInterceptorConfig,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* 该文件可自行根据业务逻辑进行调整
|
* 该文件可自行根据业务逻辑进行调整
|
||||||
*/
|
*/
|
||||||
import type { HttpResponse } from '@vben/request';
|
import type { RequestClientOptions } from '@vben/request';
|
||||||
|
|
||||||
import { useAppConfig } from '@vben/hooks';
|
import { useAppConfig } from '@vben/hooks';
|
||||||
import { preferences } from '@vben/preferences';
|
import { preferences } from '@vben/preferences';
|
||||||
@ -20,8 +20,9 @@ import { refreshTokenApi } from './core';
|
|||||||
|
|
||||||
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
|
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
|
||||||
|
|
||||||
function createRequestClient(baseURL: string) {
|
function createRequestClient(baseURL: string, options?: RequestClientOptions) {
|
||||||
const client = new RequestClient({
|
const client = new RequestClient({
|
||||||
|
...options,
|
||||||
baseURL,
|
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过期的处理
|
// token过期的处理
|
||||||
client.addResponseInterceptor(
|
client.addResponseInterceptor(
|
||||||
authenticateResponseInterceptor({
|
authenticateResponseInterceptor({
|
||||||
@ -109,6 +96,8 @@ function createRequestClient(baseURL: string) {
|
|||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const requestClient = createRequestClient(apiURL);
|
export const requestClient = createRequestClient(apiURL, {
|
||||||
|
responseReturn: 'data',
|
||||||
|
});
|
||||||
|
|
||||||
export const baseRequestClient = new RequestClient({ baseURL: apiURL });
|
export const baseRequestClient = new RequestClient({ baseURL: apiURL });
|
||||||
|
Loading…
Reference in New Issue
Block a user