🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

complex-request

Package Overview
Dependencies
Maintainers
1
Versions
41
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

complex-request - npm Package Compare versions

Comparing version
1.0.1
to
1.1.1
+198
README.md
# 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` 之间自动转换请求数据。
## 安装
```bash
npm install complex-request
```
## 快速开始
下面是一个使用 `fetch` 作为底层实现的简单示例:
```typescript
import { BaseRequest, Rule, Token } from 'complex-request';
// 1. 创建一个继承自 BaseRequest 的具体请求类
// 我们在这里为泛型 R (原始响应) 和 L (本地配置) 提供了具体的类型
class MyRequest extends BaseRequest<any, { signal?: AbortSignal }> {
$request(requestConfig) {
// 使用 fetch 实现底层的请求逻辑
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 // 传递 AbortSignal 以便取消请求
}).then(res => res.json());
}
$parseError(error) {
// 解析 fetch 抛出的错误
return { type: 'internal', msg: error.message, data: error };
}
}
// 2. 定义一套规则
const myRule = new Rule({
prop: 'api',
token: {
data: {
// 定义一个名为 'accessToken' 的 Token
accessToken: {
location: 'header', // 存储在 Header 中
require: true // 这是一个必需的 Token
}
}
},
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 => {
// ... 执行登录操作,例如弹出一个登录框
// ... 登录成功后,使用 setToken 设置新的 Token
myRequest.setToken('accessToken', 'new-token-from-server');
resolve();
});
}
});
// 3. 实例化请求对象
const myRequest = new MyRequest({
baseUrl: 'https://api.example.com',
rule: myRule
});
// 4. 发送请求
// 4. 发送请求,并传递一个本地配置
const controller = new AbortController();
myRequest.get({
url: '/user/profile',
local: { signal: controller.signal }
})
.then(response => {
console.log('用户信息:', response.data);
})
.catch(error => {
console.error('请求失败:', error);
});
// 可以在需要时取消请求
// controller.abort();
```
## 高级用法:泛型类型
`complex-request` 大量使用泛型来提供强大的类型安全。理解 `R` 和 `L` 这两个核心泛型,能帮助您更好地利用这个库。
### `R` - 原始响应 (`Raw Response`)
`R` 代表的是底层请求工具(如 `fetch`, `axios`)直接返回的、**未经 `rule.parse` 解析**的原始响应数据的类型。
在 `Rule` 的 `parse` 函数中,`response` 参数的类型就是 `R`。
```typescript
// 假设服务器返回的数据结构是 { code: number, data: T, message: string }
type ServerResponse<T> = {
code: number;
data: T;
message: string;
}
// 在定义 Rule 时,我们可以将 R 指定为 ServerResponse<any>
const myRule = new Rule<ServerResponse<any>>({
// ...
parse: (response) => { // 在这里,`response` 的类型就是 ServerResponse<any>
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-request` 库的**核心**。它就像一个交通警察,负责检查每一个从服务器返回的原始响应 (`R`),并根据您定义的业务逻辑,告诉 `BaseRequest` 下一步应该做什么。
`parse` 函数必须返回一个 `responseType` 对象,其 `status` 字段决定了整个请求的走向:
```typescript
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` 的行为**:
1. 自动触发您在 `Rule` 中定义的 `login` 函数。
2. 在 `login` 函数成功执行后(即其返回的 Promise 被 resolve),`BaseRequest` 会**自动重新发送**之前失败的那个请求。
3. 整个过程对最终的调用者是**完全透明的**。
- **`'refresh'`**: **需要刷新 Token**
- **含义**: 用户的访问令牌(Access Token)已过期,但刷新令牌(Refresh Token)仍然有效。
- **`BaseRequest` 的行为**:
1. 自动触发您在 `Rule` 中定义的 `refresh` 函数。
2. 在 `refresh` 函数成功执行后,`BaseRequest` 会**自动重新发送**之前失败的那个请求。
3. 如果 `refresh` 本身也失败了,或者刷新后重试的请求依然返回 `'refresh'`,`BaseRequest` 会自动将流程降级为 `'login'`。
通过精心设计您的 `parse` 函数,您可以以一种非常优雅和集中的方式,实现极其复杂的认证和重试逻辑。
## 依赖
- [complex-utils](https://github.com/MarAngle/complex-utils): 提供核心的工具函数和基类。
- [complex-plugin](https://github.com/MarAngle/complex-plugin): 提供 `notice` 通知插件。
import { describe, it, expect } from 'vitest';
import Token from './Token';
describe('Token', () => {
it('should be instantiated correctly with default values', () => {
const token = new Token({}, 'testToken', 'testRule');
expect(token).toBeInstanceOf(Token);
expect(token.location).toBe('body');
expect(token.require).toBeUndefined();
});
it('should store and retrieve a value from memory', () => {
const token = new Token({ value: 'my-secret-token' }, 'testToken', 'testRule');
expect(token.getValue()).toBe('my-secret-token');
});
it('should respect the require flag', () => {
const requiredToken = new Token({ require: true }, 'requiredToken', 'testRule');
const optionalToken = new Token({ require: false }, 'optionalToken', 'testRule');
expect(requiredToken.$checkValue(undefined)).toBe(false);
expect(requiredToken.$checkValue(null)).toBe(false);
expect(requiredToken.$checkValue('some-value')).toBe(true);
expect(optionalToken.$checkValue(undefined)).toBe(true);
});
it('should append value to the correct location', () => {
const headerToken = new Token({ value: 'header-val', location: 'header' }, 'hToken', 'testRule');
const bodyToken = new Token({ value: 'body-val', location: 'body' }, 'bToken', 'testRule');
const paramsToken = new Token({ value: 'params-val', location: 'params' }, 'pToken', 'testRule');
const config = {
headers: {},
data: {},
params: {}
};
headerToken.$appendValue(config as any, 'Authorization');
bodyToken.$appendValue(config as any, 'token');
paramsToken.$appendValue(config as any, 'api_key');
expect(config.headers).toEqual({ 'Authorization': 'header-val' });
expect(config.data).toEqual({ 'token': 'body-val' });
expect(config.params).toEqual({ 'api_key': 'params-val' });
});
});
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
globals: true,
environment: 'jsdom',
include: ['src/**/*.test.ts'],
},
})
+45
-47

@@ -1,87 +0,85 @@

### ToDo
- 刷新Token实现
### 1.1.1
- refactor(build): 移除项目的所有构建配置,回归到纯源码模式。
- feat(test): 集成 `Vitest` 单元测试框架。
- docs(readme): 创建详细的 `README.md` 文件。
- chore(history): 全面优化和重构 `history.md` 的格式。
### 1.0.1
- feat:修改模块加载逻辑为ES2020
- feat(module): 修改模块加载逻辑为 ES2020。
### 0.6.2
- 基于AI优化代码
- refactor(code): 基于 AI 进行全局代码优化。
### 0.6.1
- 稳定版
- chore: 稳定版升级。
### 0.5.10
- complex-plugin版本升级,逻辑优化
- chore(deps): 升级 `complex-plugin` 依赖并优化逻辑。
### 0.5.8/9
- complex-plugin版本升级
### 0.5.7
- refactor(request)!: **[非兼容性更新]** `failNotice` 重命名为 `fail`,`failNotice.local` 重命名为 `fail.intercept`。
- refactor(request): 优化 `$parseError` 返回的 `type` 类型。
- refactor(request): 将 `config` 配置项迁移到类的静态数据中。
### 0.5.6/7
- failNotice => fail
- failNotice.local => fail.intercept
- $parseError返回的type类型优化
- config配置项迁移到类静态数据中
### 0.5.5
- 优化类型传递
- refactor(types): 优化泛型类型传递。
### 0.5.4
- refreshToken的数据保存
- feat(token): 实现 `refreshToken` 的数据持久化保存。
### 0.5.2/3
- rule.login添加trigger参数,确认登录触发来源
- _request替换isRefresh为trigger参数,实现判断请求是否来自于refresh/login成功后的回调
- responseType.status添加refresh状态
- responseType.status === 'refresh'时认为需要刷新token,调用refresh进行token刷新,刷新成功后重新请求再次触发refresh则直接调用login进行登录
- responseType.status === 'login'时直接调用login进行登录
### 0.5.3
- feat(request): 实现完整的 `login` -> `refresh` -> `retry` 流程。
- `rule.login` 添加 `trigger` 参数,用于判断登录的触发来源。
- `responseType.status` 添加 `refresh` 状态,用于触发 `rule.refresh`。
- 当 `refresh` 失败或再次触发 `refresh` 时,自动降级为 `login`。
### 0.5.1
- complex-plugin版本升级
- chore(deps): 升级 `complex-plugin` 依赖。
### 0.4.4/5/6/7
- complex-plugin版本升级
### 0.4.7
- chore(deps): 升级 `complex-plugin` 依赖。
### 0.4.1/2/3
- BaseRequest的rule简化为单选,简化判断逻辑,需要多个rule可生成多个BaseRequest实例单独实现
### 0.4.3
- refactor(rule)!: **[非兼容性更新]** `BaseRequest` 的 `rule` 简化为单选,以简化逻辑。
### 0.3.6
- 优化undefined校验
- refactor(code): 优化全局的 `undefined` 校验逻辑。
### 0.3.5
- responseFormat=>responseParse
- Rule的原format函数更改为parse函数,添加新format函数在请求前实现规则的格式化
- refactor(rule)!: **[非兼容性更新]** `responseFormat` 重命名为 `responseParse`。
- refactor(rule)!: **[非兼容性更新]** `Rule` 的 `format` 函数重命名为 `parse`,并新增 `format` 函数用于请求前的格式化。
### 0.3.0/1/2/3/4
- 依赖大版本升级
### 0.3.4
- chore(deps): 依赖大版本升级。
### 0.2.1
- local传值类型实现泛型
- refactor(types): 为 `local` 参数实现泛型类型。
### 0.2.0
- 优化函数命名规则:外部函数以字母开头,内部函数以$开头,私有函数以_开头
- refactor(code): 统一函数命名规则 (外部函数、内部函数、私有函数)。
### 0.1.10
- 升级依赖,适配formatConfig
- chore(deps): 升级依赖,适配 `formatConfig`。
### 0.1.9
- BUG: 修正requestConfig.data被重置为空数据的BUG
- 类型: 扩展BaseRequest为泛型类,将返回值基础类型作为泛型传递,可以在后续扩展类中定义最终的返回值基础类型
- fix(request): 修正 `requestConfig.data` 被重置为空对象的 BUG。
- feat(types): 扩展 `BaseRequest` 为泛型类,允许定义最终的响应类型。
### 0.1.8
- 非兼容性更新: Request => BaseRequest
- 修正post/get/form/json未正确请求的BUG
- 升级依赖
- refactor(request)!: **[非兼容性更新]** `Request` 类重命名为 `BaseRequest`。
- fix(request): 修正 `post/get/form/json` 未正确设置 `method` 的 BUG。
- chore(deps): 升级依赖。
### 0.1.7
- 修正错误的依赖
- fix(deps): 修正错误的依赖项。
### 0.1.3/4/5/6
- 优化请求参数和失败错误参数
- 扩展失败信息提示
### 0.1.6
- refactor(request): 优化请求参数和失败错误处理。
- feat(request): 扩展失败信息提示。
### 0.1.2
- 扩展HTTP请求错误码
- 优化函数和类型
- feat(request): 扩展支持的 HTTP 状态码错误信息。
- refactor(code): 优化函数和类型定义。
### 0.1.1
- 实现基础功能
- feat: 项目初始化,实现基础的请求功能。
{
"name": "complex-request",
"version": "1.0.1",
"version": "1.1.1",
"description": "a complex request",

@@ -12,9 +12,13 @@ "type": "module",

"dependencies": {
"complex-plugin": "4.9.1 - 4.9.99"
"complex-plugin": "4.10.1 - 4.10.99"
},
"devDependencies": {
"typescript": "^5.2.2"
"jsdom": "^27.1.0",
"typescript": "^5.2.2",
"vitest": "^4.0.7"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"check": "tsc --noEmit",
"test": "vitest",
"coverage": "vitest run --coverage"
},

@@ -21,0 +25,0 @@ "keywords": [