Complex Request
这是一个功能强大且高度可扩展的 TypeScript 请求库,旨在通过分层、可组合的规则引擎,优雅地处理复杂的 Web 请求场景。
核心特性
- 分层架构:
BaseRequest: 抽象请求基类,负责驱动整个请求生命周期,包括 Token 处理、请求重试、错误捕获等。
Rule: 可定制的规则引擎,将所有与具体业务相关的逻辑(如响应解析、登录/刷新流程)集中管理。
Token: 封装单个 Token 的所有行为,包括值的获取、存储、附加到请求和生命周期管理。
- 强大的 Token 管理:
- 支持多个具名
Token 和一个 refreshToken。
- 支持从内存、
localStorage 和 sessionStorage 中自动获取和缓存 Token。
- 可配置 Token 的有效期、存储位置(
header, body, params)和附加键名。
- 自动化的登录和刷新流程,当 Token 失效时,可自动触发
login 或 refresh 操作,并无缝地重试原始请求。
- 高度可定制:
- 响应解析: 可通过
parse 函数,将任何格式的服务器响应,适配为统一的 { status, data, ... } 格式。
- 请求格式化: 可通过
format 函数,在请求发送前,对请求参数进行最后一次修改。
- 底层实现无关:
BaseRequest 是一个抽象类,您可以继承它,并使用任何底层的请求库(如 axios, fetch)来实现 $request 方法。
- 数据格式转换:
- 支持在
json 和 form-data 之间自动转换请求数据。
安装
npm install @complex-suite/request
快速开始
下面是一个使用 fetch 作为底层实现的简单示例:
import { BaseRequest, Rule, Token } from '@complex-suite/request';
class MyRequest extends BaseRequest<any, { signal?: AbortSignal }> {
$request(requestConfig) {
const { url, method, headers, data, params, local } = requestConfig;
const finalUrl = new URL(url);
Object.keys(params).forEach(key => finalUrl.searchParams.append(key, params[key]));
return fetch(finalUrl.toString(), {
method,
headers,
body: data,
signal: local?.signal
}).then(res => res.json());
}
$parseError(error) {
return { type: 'internal', msg: error.message, data: error };
}
}
const myRule = new Rule({
prop: 'api',
token: {
data: {
accessToken: {
location: 'header',
require: true
}
}
},
parse: (response) => {
if (response.code === 200) {
return { status: 'success', data: response.data };
} else if (response.code === 401) {
return { status: 'login', data: null, msg: '请先登录' };
} else {
return { status: 'fail', data: null, msg: response.message };
}
},
login: () => {
return new Promise(resolve => {
myRequest.setToken('accessToken', 'new-token-from-server');
resolve();
});
}
});
const myRequest = new MyRequest({
baseUrl: 'https://api.example.com',
rule: myRule
});
const controller = new AbortController();
myRequest.get({
url: '/user/profile',
local: { signal: controller.signal }
})
.then(response => {
console.log('用户信息:', response.data);
})
.catch(error => {
console.error('请求失败:', error);
});
高级用法:泛型类型
@complex-suite/request 大量使用泛型来提供强大的类型安全。理解 R 和 L 这两个核心泛型,能帮助您更好地利用这个库。
R - 原始响应 (Raw Response)
R 代表的是底层请求工具(如 fetch, axios)直接返回的、未经 rule.parse 解析的原始响应数据的类型。
在 Rule 的 parse 函数中,response 参数的类型就是 R。
type ServerResponse<T> = {
code: number;
data: T;
message: string;
}
const myRule = new Rule<ServerResponse<any>>({
parse: (response) => {
if (response.code === 200) {
return { status: 'success', data: response.data };
}
}
});
L - 本地配置 (Local Config)
L 代表的是一个可选的、用于传递给底层请求工具的、特定于实现的配置对象的类型。
这非常有用,因为不同的底层请求库(fetch, axios)有它们自己独特的配置选项(例如,axios 的 cancelToken,fetch 的 signal)。L 允许您以类型安全的方式,将这些特定于实现的配置,一路传递到您自己实现的 $request 方法中。
在上面的“快速开始”示例中,我们通过 L 泛型,为 fetch 实现了一个请求取消的功能。
parse 函数:请求的“交通警察”
parse 函数是 @complex-suite/request 库的核心。它就像一个交通警察,负责检查每一个从服务器返回的原始响应 (R),并根据您定义的业务逻辑,告诉 BaseRequest 下一步应该做什么。
parse 函数必须返回一个 responseType 对象,其 status 字段决定了整个请求的走向:
interface responseType<D = any> {
status: 'success' | 'fail' | 'login' | 'refresh'
data: D
code?: number | string
msg?: string
}
下面是 BaseRequest 对每种 status 的处理方式:
-
'success': 请求成功
- 含义: 服务器成功处理了请求,且响应数据是有效的。
BaseRequest 的行为: request 方法返回的 Promise 将被 resolve,并将 data 字段作为结果传递下去。
-
'fail': 请求失败
- 含义: 服务器返回了一个业务逻辑上的错误(例如,表单验证失败、权限不足等)。
BaseRequest 的行为: request 方法返回的 Promise 将被 reject,并将整个 responseType 对象作为错误信息传递下去。
-
'login': 需要登录
- 含义: 用户的会话已完全失效,必须重新登录。
BaseRequest 的行为:
- 自动触发您在
Rule 中定义的 login 函数。
- 在
login 函数成功执行后(即其返回的 Promise 被 resolve),BaseRequest 会自动重新发送之前失败的那个请求。
- 整个过程对最终的调用者是完全透明的。
-
'refresh': 需要刷新 Token
- 含义: 用户的访问令牌(Access Token)已过期,但刷新令牌(Refresh Token)仍然有效。
BaseRequest 的行为:
- 自动触发您在
Rule 中定义的 refresh 函数。
- 在
refresh 函数成功执行后,BaseRequest 会自动重新发送之前失败的那个请求。
- 如果
refresh 本身也失败了,或者刷新后重试的请求依然返回 'refresh',BaseRequest 会自动将流程降级为 'login'。
通过精心设计您的 parse 函数,您可以以一种非常优雅和集中的方式,实现极其复杂的认证和重试逻辑。
依赖