Browse Source

refactor(alova): 重构 AlovaClient 类并添加泛型支持

- 为 AlovaClient 类添加泛型参数 T,默认为 AlovaGenerics
- 重构请求拦截器和响应拦截器的类型定义
- 新增 ResponseData接口定义标准响应数据结构- 新增 ResponseSuccessMethod 类型定义请求成功拦截器方法
- 优化构造函数,支持传入自定义认证选项
- 为类方法添加 JSDoc 注释,提高代码可读性
- 更新 pnpm-lock.yaml,添加 alova 依赖
alova
Jin Mao 6 months ago
parent
commit
34c73b73d9
  1. 292
      packages/effects/plugins/src/alova/index.ts
  2. 25
      pnpm-lock.yaml

292
packages/effects/plugins/src/alova/index.ts

@ -9,167 +9,267 @@ import type {
RespondedAlovaGenerics,
ResponseCompleteHandler,
ResponseErrorHandler,
StatesHook,
} from 'alova';
import type {
AlovaRequestAdapterUnified,
ClientTokenAuthenticationOptions,
TokenAuthenticationResult,
} from 'alova/client';
import { createAlova } from 'alova';
import { createServerTokenAuthentication } from 'alova/client';
import adapterFetch from 'alova/fetch';
import VueHook from 'alova/vue';
type RequestMethod = (method: Method<AlovaGenerics>) => Promise<void> | void;
/**
*
*/
type RequestMethod<T extends AlovaGenerics = AlovaGenerics> = (
method: Method<T>,
) => Promise<void> | void;
class AlovaClient {
public readonly instance: Alova<AlovaGenerics>;
/**
*
*/
interface ResponseData<T = unknown> {
code?: number;
data?: T;
message?: string;
[key: string]: unknown;
}
/**
*
*/
type ResponseSuccessMethod<T = unknown> = (
json: ResponseData<T>,
) => Promise<ResponseData<T>>;
/**
* Alova HTTP客户端封装类
* @template T AlovaGenerics
*/
class AlovaClient<T extends AlovaGenerics = AlovaGenerics> {
public readonly instance: Alova<T>;
/**
*
*/
public requestInterceptor: RequestMethod<T>[] = [];
/**
*
*/
public responseCompleteInterceptor: ResponseCompleteHandler<T>[] = [];
/**
*
*/
public responseErrorInterceptor: ResponseErrorHandler<T>[] = [];
public requestInterceptor: RequestMethod[] = [];
public responseCompleteInterceptor: ResponseCompleteHandler<AlovaGenerics>[] =
[];
public responseErrorInterceptor: ResponseErrorHandler<AlovaGenerics>[] = [];
// public responseSuccessInterceptor: RespondedHandler<AlovaGenerics>[] = [];
public responseSuccessInterceptor: ((
Json: Record<string, any>,
) => Promise<Record<string, any>>)[] = [];
/**
*
*/
public responseSuccessInterceptor: ResponseSuccessMethod[] = [];
/**
*
* @param options Alova配置选项
* @param authOptions
*/
constructor(
options: AlovaOptions<AlovaGenerics>,
tokenOptions?: ClientTokenAuthenticationOptions<AlovaRequestAdapterUnified>,
options: AlovaOptions<T>,
authOptions: TokenAuthenticationResult<
StatesHook<any>,
AlovaRequestAdapterUnified
> = {},
) {
const { baseURL = '' } = options;
// const { onAuthRequired, onResponseRefreshToken } =
// createClientTokenAuthentication<typeof VueHook>({
// // ...
// ...tokenOptions,
// });
const { onAuthRequired, onResponseRefreshToken } =
createServerTokenAuthentication<typeof VueHook>({
// ...
...tokenOptions,
});
const { onAuthRequired, onResponseRefreshToken } = authOptions;
const beforeRequest = async (method: Method<T>) => {
for (const interceptor of this.requestInterceptor) {
await interceptor(method);
}
};
const responded = {
// 请求成功的拦截器
// 当使用 `alova/fetch` 请求适配器时,第一个参数接收Response对象
// 第二个参数为当前请求的method实例,你可以用它同步请求前后的配置信息
onSuccess: async (response: Response, method: Method<T>) => {
if (response.status >= 400) {
throw new Error(response.statusText);
}
const json = await response.json();
let result = json;
for (const interceptor of this.responseSuccessInterceptor) {
result = await interceptor(json);
}
return result;
},
onError: async (err: Error, method: Method<T>) => {
for (const interceptor of this.responseErrorInterceptor) {
await interceptor(err, method);
}
},
// 请求完成的拦截器
// 当你需要在请求不论是成功、失败、还是命中缓存都需要执行的逻辑时,可以在创建alova实例时指定全局的`onComplete`拦截器,例如关闭请求 loading 状态。
// 接收当前请求的method实例
onComplete: async (method: Method<T>) => {
// 处理请求完成逻辑
for (const interceptor of this.responseCompleteInterceptor) {
await interceptor(method);
}
},
};
this.instance = createAlova({
baseURL,
baseURL: '',
requestAdapter: adapterFetch(),
statesHook: VueHook,
beforeRequest: onAuthRequired(async (method) => {
for (const interceptor of this.requestInterceptor) {
await interceptor(method);
}
}),
beforeRequest: onAuthRequired
? onAuthRequired(beforeRequest)
: beforeRequest,
// 使用 responded 对象分别指定请求成功的拦截器和请求失败的拦截器
responded: onResponseRefreshToken({
// 请求成功的拦截器
// 当使用 `alova/fetch` 请求适配器时,第一个参数接收Response对象
// 第二个参数为当前请求的method实例,你可以用它同步请求前后的配置信息
onSuccess: async (response, method) => {
if (response.status >= 400) {
throw new Error(response.statusText);
}
const json = await response.json();
let result;
for (const interceptor of this.responseSuccessInterceptor) {
result = await interceptor(json);
}
return result;
},
onError: async (err, method) => {
console.log(222);
for (const interceptor of this.responseErrorInterceptor) {
await interceptor(err, method);
}
},
// 请求完成的拦截器
// 当你需要在请求不论是成功、失败、还是命中缓存都需要执行的逻辑时,可以在创建alova实例时指定全局的`onComplete`拦截器,例如关闭请求 loading 状态。
// 接收当前请求的method实例
onComplete: async (method) => {
// 处理请求完成逻辑
for (const interceptor of this.responseCompleteInterceptor) {
await interceptor(method);
}
},
}),
responded: onResponseRefreshToken
? onResponseRefreshToken(responded)
: responded,
...options,
});
}
public addRequestInterceptor(
method: (method: Method<AlovaGenerics>) => Promise<void> | void,
) {
/**
*
* @param method
*/
public addRequestInterceptor(method: RequestMethod<T>) {
this.requestInterceptor.push(method);
}
public addResponseCompleteInterceptor(
method: ResponseCompleteHandler<AlovaGenerics>,
) {
/**
*
* @param method
*/
public addResponseCompleteInterceptor(method: ResponseCompleteHandler<T>) {
this.responseCompleteInterceptor.push(method);
}
public addResponseErrorInterceptor(
method: ResponseErrorHandler<AlovaGenerics>,
) {
/**
*
* @param method
*/
public addResponseErrorInterceptor(method: ResponseErrorHandler<T>) {
this.responseErrorInterceptor.push(method);
}
public addResponseSuccessInterceptor(
method: (Json: Record<string, any>) => Promise<Record<string, any>>,
) {
/**
*
* @param method
*/
public addResponseSuccessInterceptor(method: ResponseSuccessMethod) {
this.responseSuccessInterceptor.push(method);
}
/**
* DELETE请求
* @param url
* @param data
* @param config
* @returns Method实例
*/
public delete<Responded = unknown, Transformed = unknown>(
url: string,
data?: RequestBody,
config?: AlovaMethodCreateConfig<AlovaGenerics, Responded, Transformed>,
): Method<RespondedAlovaGenerics<AlovaGenerics, Responded, Transformed>> {
config?: AlovaMethodCreateConfig<T, Responded, Transformed>,
): Method<RespondedAlovaGenerics<T, Responded, Transformed>> {
return this.instance.Delete(url, config, data);
}
/**
* GET请求
* @param url
* @param config
* @returns Method实例
*/
public get<Responded = unknown, Transformed = unknown>(
url: string,
config?: AlovaMethodCreateConfig<AlovaGenerics, Responded, Transformed>,
): Method<RespondedAlovaGenerics<AlovaGenerics, Responded, Transformed>> {
config?: AlovaMethodCreateConfig<T, Responded, Transformed>,
): Method<RespondedAlovaGenerics<T, Responded, Transformed>> {
return this.instance.Get(url, config);
}
/**
* HEAD请求
* @param url
* @param config
* @returns Method实例
*/
public head<Responded = unknown, Transformed = unknown>(
url: string,
config?: AlovaMethodCreateConfig<AlovaGenerics, Responded, Transformed>,
): Method<RespondedAlovaGenerics<AlovaGenerics, Responded, Transformed>> {
config?: AlovaMethodCreateConfig<T, Responded, Transformed>,
): Method<RespondedAlovaGenerics<T, Responded, Transformed>> {
return this.instance.Head(url, config);
}
/**
* OPTIONS请求
* @param url
* @param config
* @returns Method实例
*/
public options<Responded = unknown, Transformed = unknown>(
url: string,
config?: AlovaMethodCreateConfig<AlovaGenerics, Responded, Transformed>,
): Method<RespondedAlovaGenerics<AlovaGenerics, Responded, Transformed>> {
config?: AlovaMethodCreateConfig<T, Responded, Transformed>,
): Method<RespondedAlovaGenerics<T, Responded, Transformed>> {
return this.instance.Options(url, config);
}
/**
* PATCH请求
* @param url
* @param data
* @param config
* @returns Method实例
*/
public patch<Responded = unknown, Transformed = unknown>(
url: string,
data?: RequestBody,
config?: AlovaMethodCreateConfig<AlovaGenerics, Responded, Transformed>,
): Method<RespondedAlovaGenerics<AlovaGenerics, Responded, Transformed>> {
config?: AlovaMethodCreateConfig<T, Responded, Transformed>,
): Method<RespondedAlovaGenerics<T, Responded, Transformed>> {
return this.instance.Patch(url, data, config);
}
/**
* POST请求
* @param url
* @param data
* @param config
* @returns Method实例
*/
public post<Responded = unknown, Transformed = unknown>(
url: string,
data?: RequestBody,
config?: AlovaMethodCreateConfig<AlovaGenerics, Responded, Transformed>,
): Method<RespondedAlovaGenerics<AlovaGenerics, Responded, Transformed>> {
config?: AlovaMethodCreateConfig<T, Responded, Transformed>,
): Method<RespondedAlovaGenerics<T, Responded, Transformed>> {
return this.instance.Post(url, data, config);
}
/**
* PUT请求
* @param url
* @param data
* @param config
* @returns Method实例
*/
public put<Responded = unknown, Transformed = unknown>(
url: string,
data?: RequestBody,
config?: AlovaMethodCreateConfig<AlovaGenerics, Responded, Transformed>,
): Method<RespondedAlovaGenerics<AlovaGenerics, Responded, Transformed>> {
config?: AlovaMethodCreateConfig<T, Responded, Transformed>,
): Method<RespondedAlovaGenerics<T, Responded, Transformed>> {
return this.instance.Put(url, data, config);
}
/**
*
* @param config
* @returns Method实例
*/
public request<Responded = unknown, Transformed = unknown>(
config: AlovaMethodCommonConfig<AlovaGenerics, Responded, Transformed>,
): Method<RespondedAlovaGenerics<AlovaGenerics, Responded, Transformed>> {
config: AlovaMethodCommonConfig<T, Responded, Transformed>,
): Method<RespondedAlovaGenerics<T, Responded, Transformed>> {
return this.instance.Request(config);
}
}
export { AlovaClient };
export { AlovaClient, VueHook };
export * from 'alova';
export * from 'alova/client';

25
pnpm-lock.yaml

@ -153,6 +153,9 @@ catalogs:
'@vueuse/motion':
specifier: ^3.0.3
version: 3.0.3
alova:
specifier: ^3.3.4
version: 3.3.4
ant-design-vue:
specifier: ^4.2.6
version: 4.2.6
@ -1705,6 +1708,9 @@ importers:
'@vueuse/motion':
specifier: 'catalog:'
version: 3.0.3(magicast@0.3.5)(vue@3.5.17(typescript@5.8.3))
alova:
specifier: 'catalog:'
version: 3.3.4
echarts:
specifier: 'catalog:'
version: 5.6.0
@ -2011,6 +2017,9 @@ packages:
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
engines: {node: '>=10'}
'@alova/shared@1.3.1':
resolution: {integrity: sha512-ijSOaFLUFcVzMKSY3avoEE5C03/p9atjMDPBwvNkwnzaCrhv6/m4A121NdadF8YlHCRuifyYfz90IyEdMXTsJg==}
'@ampproject/remapping@2.3.0':
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
engines: {node: '>=6.0.0'}
@ -5559,6 +5568,10 @@ packages:
alien-signals@1.0.13:
resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==}
alova@3.3.4:
resolution: {integrity: sha512-UKKqXdvf8aQ4C7m3brO77YWe5CDz8N59PdAUz7M8gowKUUXTutbk0Vk5DRBrCe0hMUyyNMUhdCZ38llGxCViyQ==}
engines: {node: '>= 18.0.0'}
ansi-align@3.0.1:
resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==}
@ -9883,6 +9896,9 @@ packages:
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
engines: {node: '>= 0.6'}
rate-limiter-flexible@5.0.5:
resolution: {integrity: sha512-+/dSQfo+3FYwYygUs/V2BBdwGa9nFtakDwKt4l0bnvNB53TNT++QSFewwHX9qXrZJuMe9j+TUaU21lm5ARgqdQ==}
rc9@2.1.2:
resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==}
@ -11857,6 +11873,8 @@ snapshots:
'@alloc/quick-lru@5.2.0': {}
'@alova/shared@1.3.1': {}
'@ampproject/remapping@2.3.0':
dependencies:
'@jridgewell/gen-mapping': 0.3.12
@ -15912,6 +15930,11 @@ snapshots:
alien-signals@1.0.13: {}
alova@3.3.4:
dependencies:
'@alova/shared': 1.3.1
rate-limiter-flexible: 5.0.5
ansi-align@3.0.1:
dependencies:
string-width: 4.2.3
@ -20549,6 +20572,8 @@ snapshots:
range-parser@1.2.1: {}
rate-limiter-flexible@5.0.5: {}
rc9@2.1.2:
dependencies:
defu: 6.1.4

Loading…
Cancel
Save