@be-link/request 1.45.1-beta.8 → 1.45.1-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +288 -365
- package/dist/BeLinkRequest.d.ts +207 -0
- package/dist/BeLinkRequest.d.ts.map +1 -0
- package/dist/index.cjs.js +721 -5356
- package/dist/index.d.ts +88 -33
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +717 -5331
- package/dist/interceptors/index.d.ts +10 -0
- package/dist/interceptors/index.d.ts.map +1 -0
- package/dist/interceptors/request.d.ts +50 -0
- package/dist/interceptors/request.d.ts.map +1 -0
- package/dist/interceptors/response.d.ts +52 -0
- package/dist/interceptors/response.d.ts.map +1 -0
- package/dist/services/EncryptionService.d.ts +120 -0
- package/dist/services/EncryptionService.d.ts.map +1 -0
- package/dist/services/TimeSyncService.d.ts +117 -0
- package/dist/services/TimeSyncService.d.ts.map +1 -0
- package/dist/services/index.d.ts +10 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/types.d.ts +268 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +1 -2
- package/dist/application/clients/BeLinkClient.d.ts +0 -82
- package/dist/application/clients/BeLinkClient.d.ts.map +0 -1
- package/dist/application/clients/HttpClient.d.ts +0 -64
- package/dist/application/clients/HttpClient.d.ts.map +0 -1
- package/dist/application/clients/LiveClient.d.ts +0 -52
- package/dist/application/clients/LiveClient.d.ts.map +0 -1
- package/dist/application/interceptors/AuthInterceptor.d.ts +0 -23
- package/dist/application/interceptors/AuthInterceptor.d.ts.map +0 -1
- package/dist/application/interceptors/TimeInterceptor.d.ts +0 -21
- package/dist/application/interceptors/TimeInterceptor.d.ts.map +0 -1
- package/dist/application/interceptors/TokenInterceptor.d.ts +0 -26
- package/dist/application/interceptors/TokenInterceptor.d.ts.map +0 -1
- package/dist/application/services/UserInfoService.d.ts +0 -33
- package/dist/application/services/UserInfoService.d.ts.map +0 -1
- package/dist/cloudbase/index.d.ts +0 -27
- package/dist/cloudbase/index.d.ts.map +0 -1
- package/dist/cloudbase/types.d.ts +0 -63
- package/dist/cloudbase/types.d.ts.map +0 -1
- package/dist/core/constants/index.d.ts +0 -64
- package/dist/core/constants/index.d.ts.map +0 -1
- package/dist/core/errors/index.d.ts +0 -42
- package/dist/core/errors/index.d.ts.map +0 -1
- package/dist/core/interfaces/IConfigManager.d.ts +0 -31
- package/dist/core/interfaces/IConfigManager.d.ts.map +0 -1
- package/dist/core/interfaces/IEncryptionService.d.ts +0 -28
- package/dist/core/interfaces/IEncryptionService.d.ts.map +0 -1
- package/dist/core/interfaces/IHttpClient.d.ts +0 -43
- package/dist/core/interfaces/IHttpClient.d.ts.map +0 -1
- package/dist/core/interfaces/index.d.ts +0 -4
- package/dist/core/interfaces/index.d.ts.map +0 -1
- package/dist/core/types/config.types.d.ts +0 -65
- package/dist/core/types/config.types.d.ts.map +0 -1
- package/dist/core/types/index.d.ts +0 -5
- package/dist/core/types/index.d.ts.map +0 -1
- package/dist/core/types/interceptor.types.d.ts +0 -45
- package/dist/core/types/interceptor.types.d.ts.map +0 -1
- package/dist/core/types/request.types.d.ts +0 -65
- package/dist/core/types/request.types.d.ts.map +0 -1
- package/dist/core/types/response.types.d.ts +0 -29
- package/dist/core/types/response.types.d.ts.map +0 -1
- package/dist/infrastructure/adapters/AxiosAdapter.d.ts +0 -51
- package/dist/infrastructure/adapters/AxiosAdapter.d.ts.map +0 -1
- package/dist/infrastructure/services/ConfigManager.d.ts +0 -52
- package/dist/infrastructure/services/ConfigManager.d.ts.map +0 -1
- package/dist/infrastructure/services/EncryptionService.d.ts +0 -34
- package/dist/infrastructure/services/EncryptionService.d.ts.map +0 -1
- package/dist/infrastructure/services/TimeSyncService.d.ts +0 -43
- package/dist/infrastructure/services/TimeSyncService.d.ts.map +0 -1
- package/dist/infrastructure/utils/env.utils.d.ts +0 -13
- package/dist/infrastructure/utils/env.utils.d.ts.map +0 -1
- package/dist/presets/belink.preset.d.ts +0 -4
- package/dist/presets/belink.preset.d.ts.map +0 -1
- package/dist/presets/index.d.ts +0 -4
- package/dist/presets/index.d.ts.map +0 -1
- package/dist/presets/live.preset.d.ts +0 -4
- package/dist/presets/live.preset.d.ts.map +0 -1
- package/dist/presets/request.preset.d.ts +0 -9
- package/dist/presets/request.preset.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,499 +1,422 @@
|
|
|
1
1
|
# @be-link/request
|
|
2
2
|
|
|
3
|
-
共比邻 HTTP
|
|
3
|
+
共比邻 HTTP 请求库,提供简洁的 HTTP 请求封装,支持时间同步和 Token 加密。
|
|
4
4
|
|
|
5
5
|
## 特性
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
7
|
+
- 单例模式,全局统一配置
|
|
8
|
+
- 服务器时间同步(解决客户端时间不准问题)
|
|
9
|
+
- Token AES 加密(防重放攻击)
|
|
10
|
+
- 请求/响应拦截器
|
|
10
11
|
- TypeScript 类型支持
|
|
11
|
-
-
|
|
12
|
-
-
|
|
12
|
+
- 所有参数由应用方传递,灵活配置
|
|
13
|
+
- 自定义 Header 名称(Token、UserId)
|
|
14
|
+
- 并发请求时自动合并时间同步(防止重复请求)
|
|
15
|
+
- 使用 `performance.now()` 防止客户端系统时间篡改
|
|
13
16
|
|
|
14
17
|
## 安装
|
|
15
18
|
|
|
16
19
|
```bash
|
|
17
|
-
# 基础安装(包含所有依赖)
|
|
18
20
|
pnpm add @be-link/request axios crypto-js
|
|
19
|
-
|
|
20
|
-
# 如果需要使用云函数调用 (callFunction)
|
|
21
|
-
pnpm add @be-link/request axios crypto-js @cloudbase/js-sdk
|
|
22
21
|
```
|
|
23
22
|
|
|
24
23
|
### 依赖说明
|
|
25
24
|
|
|
26
|
-
| 依赖
|
|
27
|
-
|
|
|
28
|
-
| `axios`
|
|
29
|
-
| `crypto-js`
|
|
30
|
-
| `@cloudbase/js-sdk` | ⚠️ 可选 | 仅 `callFunction` 需要 |
|
|
25
|
+
| 依赖 | 是否必须 | 说明 |
|
|
26
|
+
| ----------- | -------- | ----------------- |
|
|
27
|
+
| `axios` | ✅ 必须 | HTTP 客户端 |
|
|
28
|
+
| `crypto-js` | ✅ 必须 | Token 加密(AES) |
|
|
31
29
|
|
|
32
|
-
|
|
30
|
+
## 快速开始
|
|
33
31
|
|
|
34
|
-
|
|
32
|
+
### 基本用法
|
|
35
33
|
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
│ 应用层 (Application) │
|
|
39
|
-
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
|
|
40
|
-
│ │ beLinkRequest │ │ beLinkLiveRequest│ │ request │ │
|
|
41
|
-
│ │ (企业级客户端) │ │ (直播客户端) │ │ (基础请求) │ │
|
|
42
|
-
│ └────────┬─────────┘ └────────┬─────────┘ └────────┬─────────┘ │
|
|
43
|
-
└───────────│─────────────────────│─────────────────────│─────────────┘
|
|
44
|
-
│ │ │
|
|
45
|
-
▼ ▼ │
|
|
46
|
-
┌─────────────────────────────────────────────────────────────────────┐
|
|
47
|
-
│ 客户端层 (Clients) │
|
|
48
|
-
│ ┌──────────────────┐ ┌──────────────────┐ │
|
|
49
|
-
│ │ BeLinkClient │ │ LiveClient │ │
|
|
50
|
-
│ │ - Token 加密 │ │ - 简化版 │ │
|
|
51
|
-
│ │ - 时间同步 │ │ - 无认证逻辑 │ │
|
|
52
|
-
│ │ - 401 处理 │ │ │ │
|
|
53
|
-
│ └────────┬─────────┘ └────────┬─────────┘ │
|
|
54
|
-
└───────────│─────────────────────│───────────────────────────────────┘
|
|
55
|
-
│ │
|
|
56
|
-
▼ ▼
|
|
57
|
-
┌─────────────────────────────────────────────────────────────────────┐
|
|
58
|
-
│ 基类层 (HttpClient) │
|
|
59
|
-
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|
60
|
-
│ │ - get/post/put/delete 方法 │ │
|
|
61
|
-
│ │ - 拦截器管理 (addRequestInterceptor/addResponseInterceptor) │ │
|
|
62
|
-
│ │ - 请求上下文管理 │ │
|
|
63
|
-
│ └──────────────────────────────┬──────────────────────────────┘ │
|
|
64
|
-
└─────────────────────────────────│───────────────────────────────────┘
|
|
65
|
-
│
|
|
66
|
-
▼
|
|
67
|
-
┌─────────────────────────────────────────────────────────────────────┐
|
|
68
|
-
│ 适配器层 (AxiosAdapter) │
|
|
69
|
-
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|
70
|
-
│ │ - 封装 Axios 实例 │ │
|
|
71
|
-
│ │ - 统一错误处理 (HttpError/NetworkError/TimeoutError) │ │
|
|
72
|
-
│ │ - 请求/响应转换 │ │
|
|
73
|
-
│ └─────────────────────────────────────────────────────────────┘ │
|
|
74
|
-
└─────────────────────────────────────────────────────────────────────┘
|
|
75
|
-
```
|
|
34
|
+
```typescript
|
|
35
|
+
import { beLinkRequest } from '@be-link/request';
|
|
76
36
|
|
|
77
|
-
|
|
37
|
+
// 1. 初始化(应用启动时调用一次)
|
|
38
|
+
beLinkRequest.init({
|
|
39
|
+
baseURL: 'https://api.example.com',
|
|
40
|
+
timeout: 30000,
|
|
41
|
+
});
|
|
78
42
|
|
|
43
|
+
// 2. 发起请求
|
|
44
|
+
const users = await beLinkRequest.get('/api/users');
|
|
45
|
+
const result = await beLinkRequest.post('/api/users', { name: '张三' });
|
|
79
46
|
```
|
|
80
|
-
每次请求时:
|
|
81
|
-
┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
|
82
|
-
│ TokenProvider│────>│ EncryptionService│────>│ 请求头 │
|
|
83
|
-
│ () => token │ │ AES(token|+|time)│ │ x-belink-auth.. │
|
|
84
|
-
└─────────────┘ └──────────────────┘ └─────────────────┘
|
|
85
|
-
│
|
|
86
|
-
▼
|
|
87
|
-
TimeSyncService
|
|
88
|
-
(服务器时间校正)
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
## 客户端选择指南
|
|
92
|
-
|
|
93
|
-
| 客户端 | 使用场景 | Token 加密 | 时间同步 | 401 处理 |
|
|
94
|
-
| ------------------- | -------------------- | :--------: | :------: | :------: |
|
|
95
|
-
| `beLinkRequest` | 需要认证的业务 API | ✅ | ✅ | ✅ |
|
|
96
|
-
| `beLinkLiveRequest` | 直播、无需认证的场景 | ❌ | ❌ | ❌ |
|
|
97
|
-
| `request` | 第三方 API、简单请求 | ❌ | ❌ | ❌ |
|
|
98
|
-
|
|
99
|
-
## 快速开始
|
|
100
47
|
|
|
101
|
-
###
|
|
48
|
+
### 完整配置
|
|
102
49
|
|
|
103
50
|
```typescript
|
|
104
51
|
import { beLinkRequest } from '@be-link/request';
|
|
105
52
|
|
|
106
|
-
// 初始化客户端
|
|
107
53
|
beLinkRequest.init({
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
54
|
+
// 基础配置
|
|
55
|
+
baseURL: 'https://api.example.com',
|
|
56
|
+
timeout: 30000,
|
|
57
|
+
headers: {
|
|
58
|
+
'X-Custom-Header': 'value',
|
|
59
|
+
},
|
|
114
60
|
|
|
115
|
-
//
|
|
116
|
-
|
|
61
|
+
// 时间同步配置
|
|
62
|
+
timeSync: {
|
|
63
|
+
enabled: true,
|
|
64
|
+
syncUrl: 'https://api.example.com/api/time',
|
|
65
|
+
syncGapTime: 50000, // 同步间隔阈值(毫秒)
|
|
66
|
+
storagePrefix: 'myapp', // 存储 key 前缀
|
|
67
|
+
},
|
|
117
68
|
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
|
|
69
|
+
// 加密配置
|
|
70
|
+
encryption: {
|
|
71
|
+
enabled: true,
|
|
72
|
+
key: 'your-aes-key-16ch', // 16 位 AES 密钥
|
|
73
|
+
iv: 'your-aes-iv-16ch', // 16 位 AES IV
|
|
74
|
+
},
|
|
121
75
|
|
|
122
|
-
|
|
76
|
+
// Token 获取函数
|
|
77
|
+
getToken: () => localStorage.getItem('token'),
|
|
123
78
|
|
|
124
|
-
|
|
125
|
-
|
|
79
|
+
// 用户 ID 获取函数
|
|
80
|
+
getUserId: () => localStorage.getItem('userId'),
|
|
126
81
|
|
|
127
|
-
//
|
|
128
|
-
|
|
82
|
+
// 自定义 Header 名称(可选)
|
|
83
|
+
tokenHeaderName: 'X-BeLink-Token', // 默认值
|
|
84
|
+
userIdHeaderName: 'X-BeLink-UserId', // 默认值
|
|
129
85
|
|
|
130
|
-
//
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
86
|
+
// 自定义请求拦截器
|
|
87
|
+
onRequest: (config) => {
|
|
88
|
+
console.log('请求发出:', config.url);
|
|
89
|
+
return config;
|
|
90
|
+
},
|
|
135
91
|
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
92
|
+
// 自定义响应拦截器
|
|
93
|
+
onResponse: (response) => {
|
|
94
|
+
console.log('响应收到:', response);
|
|
95
|
+
return response;
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
// 自定义错误处理
|
|
99
|
+
onError: (error) => {
|
|
100
|
+
if (error.response?.status === 401) {
|
|
101
|
+
window.location.href = '/login';
|
|
102
|
+
}
|
|
103
|
+
throw error;
|
|
104
|
+
},
|
|
143
105
|
});
|
|
144
106
|
```
|
|
145
107
|
|
|
146
108
|
## API 文档
|
|
147
109
|
|
|
148
|
-
### beLinkRequest
|
|
110
|
+
### beLinkRequest
|
|
149
111
|
|
|
150
|
-
|
|
112
|
+
单例请求客户端,提供以下方法:
|
|
151
113
|
|
|
152
|
-
|
|
114
|
+
#### init(options)
|
|
153
115
|
|
|
154
|
-
|
|
155
|
-
- 服务器时间同步(解决客户端时间不准问题)
|
|
156
|
-
- 401 自动处理(跳转登录页或执行回调)
|
|
116
|
+
初始化请求客户端,必须在使用其他方法之前调用。
|
|
157
117
|
|
|
158
118
|
```typescript
|
|
159
|
-
import { beLinkRequest } from '@be-link/request';
|
|
160
|
-
|
|
161
|
-
// 初始化(必须在使用前调用)
|
|
162
119
|
beLinkRequest.init({
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
loginPath: '/login',
|
|
166
|
-
tokenHeaderName: 'x-belink-authorization', // 请求头名称
|
|
167
|
-
tokenStorageKey: 'belink_token', // localStorage 键名
|
|
120
|
+
baseURL: 'https://api.example.com',
|
|
121
|
+
// ... 其他配置
|
|
168
122
|
});
|
|
123
|
+
```
|
|
169
124
|
|
|
170
|
-
|
|
171
|
-
beLinkRequest.setToken(token: string): void // 设置 Token
|
|
172
|
-
beLinkRequest.getToken(): string | null // 获取 Token
|
|
173
|
-
beLinkRequest.setupTokenListener(tokenKey): () => void // 监听 Token 变化
|
|
125
|
+
#### HTTP 方法
|
|
174
126
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
beLinkRequest.
|
|
178
|
-
beLinkRequest.put<T>(url: string, data?, config?): Promise<ApiResponse<T>>
|
|
179
|
-
beLinkRequest.delete<T>(url: string, config?): Promise<ApiResponse<T>>
|
|
127
|
+
```typescript
|
|
128
|
+
// GET 请求
|
|
129
|
+
beLinkRequest.get<T>(url: string, config?: AxiosRequestConfig): Promise<T>
|
|
180
130
|
|
|
181
|
-
//
|
|
182
|
-
beLinkRequest.
|
|
131
|
+
// POST 请求
|
|
132
|
+
beLinkRequest.post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>
|
|
183
133
|
|
|
184
|
-
//
|
|
185
|
-
beLinkRequest.
|
|
186
|
-
beLinkRequest.removeRequestTime(): Promise<void> // 清除请求时间
|
|
187
|
-
```
|
|
134
|
+
// PUT 请求
|
|
135
|
+
beLinkRequest.put<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>
|
|
188
136
|
|
|
189
|
-
|
|
137
|
+
// PATCH 请求
|
|
138
|
+
beLinkRequest.patch<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>
|
|
190
139
|
|
|
191
|
-
|
|
140
|
+
// DELETE 请求
|
|
141
|
+
beLinkRequest.delete<T>(url: string, config?: AxiosRequestConfig): Promise<T>
|
|
192
142
|
|
|
193
|
-
|
|
143
|
+
// 通用请求
|
|
144
|
+
beLinkRequest.request<T>(config: AxiosRequestConfig): Promise<T>
|
|
145
|
+
```
|
|
194
146
|
|
|
195
|
-
|
|
196
|
-
- 无时间同步
|
|
197
|
-
- 无 401 处理
|
|
198
|
-
- 支持配置 baseUrl 和 tokenHeaderName
|
|
147
|
+
#### 其他方法
|
|
199
148
|
|
|
200
149
|
```typescript
|
|
201
|
-
|
|
150
|
+
// 获取时间同步服务实例
|
|
151
|
+
beLinkRequest.getTimeSyncService(): TimeSyncService | null
|
|
202
152
|
|
|
203
|
-
//
|
|
204
|
-
|
|
205
|
-
baseUrl: 'https://live-api.example.com',
|
|
206
|
-
tokenHeaderName: 'x-live-authorization', // 可选
|
|
207
|
-
});
|
|
153
|
+
// 获取加密服务实例
|
|
154
|
+
beLinkRequest.getEncryptionService(): EncryptionService | null
|
|
208
155
|
|
|
209
|
-
//
|
|
210
|
-
|
|
211
|
-
|
|
156
|
+
// 获取 Axios 实例
|
|
157
|
+
beLinkRequest.getAxiosInstance(): AxiosInstance | null
|
|
158
|
+
|
|
159
|
+
// 重置客户端(清除配置,需重新 init)
|
|
160
|
+
beLinkRequest.reset(): void
|
|
212
161
|
```
|
|
213
162
|
|
|
214
|
-
###
|
|
163
|
+
### 配置选项 RequestOptions
|
|
215
164
|
|
|
216
|
-
|
|
165
|
+
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
|
166
|
+
| ---------------- | ----------------------------------- | :--: | --------------- | ------------------ |
|
|
167
|
+
| baseURL | `string` | ✅ | - | API 基础地址 |
|
|
168
|
+
| timeout | `number` | ❌ | 30000 | 请求超时时间(ms) |
|
|
169
|
+
| headers | `Record<string, string>` | ❌ | {} | 默认请求头 |
|
|
170
|
+
| timeSync | `TimeSyncConfig` | ❌ | - | 时间同步配置 |
|
|
171
|
+
| encryption | `EncryptionConfig` | ❌ | - | 加密配置 |
|
|
172
|
+
| getToken | `() => string \| null \| undefined` | ❌ | - | Token 获取函数 |
|
|
173
|
+
| getUserId | `() => string \| null \| undefined` | ❌ | - | 用户 ID 获取函数 |
|
|
174
|
+
| tokenHeaderName | `string` | ❌ | X-BeLink-Token | Token Header 名称 |
|
|
175
|
+
| userIdHeaderName | `string` | ❌ | X-BeLink-UserId | UserId Header 名称 |
|
|
176
|
+
| onRequest | `(config) => config` | ❌ | - | 自定义请求拦截器 |
|
|
177
|
+
| onResponse | `(response) => response` | ❌ | - | 自定义响应拦截器 |
|
|
178
|
+
| onError | `(error) => any` | ❌ | - | 自定义错误处理 |
|
|
217
179
|
|
|
218
|
-
|
|
219
|
-
import { request } from '@be-link/request';
|
|
180
|
+
### TimeSyncConfig
|
|
220
181
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
182
|
+
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
|
183
|
+
| ------------- | --------- | :--: | ------ | --------------------- |
|
|
184
|
+
| enabled | `boolean` | ❌ | true | 是否启用时间同步 |
|
|
185
|
+
| syncUrl | `string` | ✅ | - | 时间同步接口 URL |
|
|
186
|
+
| syncGapTime | `number` | ❌ | 50000 | 同步间隔阈值(毫秒) |
|
|
187
|
+
| storagePrefix | `string` | ❌ | - | localStorage key 前缀 |
|
|
188
|
+
|
|
189
|
+
### EncryptionConfig
|
|
190
|
+
|
|
191
|
+
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
|
192
|
+
| ------- | --------- | :--: | ------ | -------------- |
|
|
193
|
+
| enabled | `boolean` | ❌ | true | 是否启用加密 |
|
|
194
|
+
| key | `string` | ✅ | - | 16 位 AES 密钥 |
|
|
195
|
+
| iv | `string` | ✅ | - | 16 位 AES IV |
|
|
227
196
|
|
|
228
|
-
|
|
197
|
+
## 使用示例
|
|
229
198
|
|
|
230
|
-
|
|
199
|
+
### 基础请求
|
|
231
200
|
|
|
232
201
|
```typescript
|
|
233
|
-
import {
|
|
234
|
-
|
|
235
|
-
// 调用云函数
|
|
236
|
-
const result = await callFunction({
|
|
237
|
-
name: 'cloud-function-name',
|
|
238
|
-
data: {
|
|
239
|
-
moduleName: 'biz',
|
|
240
|
-
serviceName: 'main',
|
|
241
|
-
funcName: 'getUserInfo',
|
|
242
|
-
param: { userId: '123' },
|
|
243
|
-
},
|
|
244
|
-
});
|
|
202
|
+
import { beLinkRequest } from '@be-link/request';
|
|
245
203
|
|
|
246
|
-
//
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
data: {
|
|
250
|
-
moduleName: 'biz',
|
|
251
|
-
serviceName: 'main',
|
|
252
|
-
funcName: 'createOrder',
|
|
253
|
-
param: { productId: '456' },
|
|
254
|
-
operateUser: { name: 'staffName' }, // 可选,会自动注入
|
|
255
|
-
},
|
|
204
|
+
// 初始化
|
|
205
|
+
beLinkRequest.init({
|
|
206
|
+
baseURL: 'https://api.example.com',
|
|
256
207
|
});
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
## 多服务配置
|
|
260
208
|
|
|
261
|
-
|
|
209
|
+
// GET 请求
|
|
210
|
+
const users = await beLinkRequest.get('/api/users');
|
|
262
211
|
|
|
263
|
-
|
|
264
|
-
|
|
212
|
+
// 带参数的 GET 请求
|
|
213
|
+
const user = await beLinkRequest.get('/api/users/1', {
|
|
214
|
+
params: { include: 'profile' },
|
|
215
|
+
});
|
|
265
216
|
|
|
266
|
-
//
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
baseUrl: 'https://api-a.example.com',
|
|
271
|
-
tokenHeaderName: 'x-service-a-auth',
|
|
272
|
-
tokenStorageKey: 'service_a_token',
|
|
217
|
+
// POST 请求
|
|
218
|
+
const newUser = await beLinkRequest.post('/api/users', {
|
|
219
|
+
name: '张三',
|
|
220
|
+
age: 25,
|
|
273
221
|
});
|
|
274
222
|
|
|
275
|
-
//
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
mode: 'production',
|
|
279
|
-
baseUrl: 'https://api-b.example.com',
|
|
280
|
-
tokenHeaderName: 'x-service-b-auth',
|
|
281
|
-
tokenStorageKey: 'service_b_token',
|
|
223
|
+
// PUT 请求
|
|
224
|
+
const updated = await beLinkRequest.put('/api/users/1', {
|
|
225
|
+
name: '李四',
|
|
282
226
|
});
|
|
283
227
|
|
|
284
|
-
//
|
|
285
|
-
|
|
286
|
-
// "service_a_token": "token_for_service_a",
|
|
287
|
-
// "service_b_token": "token_for_service_b"
|
|
288
|
-
// }
|
|
228
|
+
// DELETE 请求
|
|
229
|
+
await beLinkRequest.delete('/api/users/1');
|
|
289
230
|
```
|
|
290
231
|
|
|
291
|
-
|
|
232
|
+
### 带类型的请求
|
|
292
233
|
|
|
293
|
-
|
|
234
|
+
```typescript
|
|
235
|
+
interface User {
|
|
236
|
+
id: number;
|
|
237
|
+
name: string;
|
|
238
|
+
age: number;
|
|
239
|
+
}
|
|
294
240
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
| tokenHeaderName | `string` | ❌ | `'x-belink-authorization'` | Token 请求头名称 |
|
|
301
|
-
| tokenStorageKey | `string` | ❌ | `'token'` | localStorage 存储键名 |
|
|
241
|
+
interface ApiResult<T> {
|
|
242
|
+
success: boolean;
|
|
243
|
+
data: T;
|
|
244
|
+
message?: string;
|
|
245
|
+
}
|
|
302
246
|
|
|
303
|
-
|
|
247
|
+
// 指定响应类型
|
|
248
|
+
const result = await beLinkRequest.get<ApiResult<User[]>>('/api/users');
|
|
249
|
+
console.log(result.data); // User[]
|
|
304
250
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
params?: Record<string, any>; // URL 参数
|
|
311
|
-
data?: D; // 请求体
|
|
312
|
-
timeout?: number; // 超时时间(毫秒)
|
|
313
|
-
withCredentials?: boolean; // 是否携带凭证
|
|
314
|
-
customToken?: string; // 自定义 Token
|
|
315
|
-
}
|
|
251
|
+
const user = await beLinkRequest.post<ApiResult<User>>('/api/users', {
|
|
252
|
+
name: '张三',
|
|
253
|
+
age: 25,
|
|
254
|
+
});
|
|
255
|
+
console.log(user.data); // User
|
|
316
256
|
```
|
|
317
257
|
|
|
318
|
-
###
|
|
258
|
+
### 错误处理
|
|
319
259
|
|
|
320
260
|
```typescript
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
261
|
+
beLinkRequest.init({
|
|
262
|
+
baseURL: 'https://api.example.com',
|
|
263
|
+
onError: (error) => {
|
|
264
|
+
// 处理 401 未授权
|
|
265
|
+
if (error.response?.status === 401) {
|
|
266
|
+
localStorage.removeItem('token');
|
|
267
|
+
window.location.href = '/login';
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// 处理业务错误
|
|
272
|
+
if (error.data?.message) {
|
|
273
|
+
alert(error.data.message);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
throw error;
|
|
277
|
+
},
|
|
278
|
+
});
|
|
327
279
|
```
|
|
328
280
|
|
|
329
|
-
|
|
281
|
+
### 多实例场景
|
|
330
282
|
|
|
331
|
-
|
|
283
|
+
如果需要创建多个独立的请求实例,可以使用 `BeLinkRequest` 类:
|
|
332
284
|
|
|
333
285
|
```typescript
|
|
334
|
-
import {
|
|
286
|
+
import { BeLinkRequest } from '@be-link/request';
|
|
335
287
|
|
|
336
|
-
//
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
onRequest: async (context) => {
|
|
341
|
-
// 修改请求配置
|
|
342
|
-
context.config.headers['X-Custom'] = 'value';
|
|
343
|
-
return context;
|
|
344
|
-
},
|
|
288
|
+
// 创建实例 A
|
|
289
|
+
const serviceA = new BeLinkRequest();
|
|
290
|
+
serviceA.init({
|
|
291
|
+
baseURL: 'https://api-a.example.com',
|
|
345
292
|
});
|
|
346
293
|
|
|
347
|
-
//
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
onResponse: async (context) => {
|
|
352
|
-
console.log('Response:', context.response);
|
|
353
|
-
return context;
|
|
354
|
-
},
|
|
355
|
-
onResponseError: async (error) => {
|
|
356
|
-
console.error('Error:', error);
|
|
357
|
-
throw error;
|
|
358
|
-
},
|
|
294
|
+
// 创建实例 B
|
|
295
|
+
const serviceB = new BeLinkRequest();
|
|
296
|
+
serviceB.init({
|
|
297
|
+
baseURL: 'https://api-b.example.com',
|
|
359
298
|
});
|
|
360
299
|
|
|
361
|
-
//
|
|
362
|
-
|
|
300
|
+
// 分别使用
|
|
301
|
+
const dataA = await serviceA.get('/api/data');
|
|
302
|
+
const dataB = await serviceB.get('/api/data');
|
|
363
303
|
```
|
|
364
304
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
| 拦截器 | 优先级 | 功能 |
|
|
368
|
-
| ---------------- | :----: | -------------------------- |
|
|
369
|
-
| TimeInterceptor | 1 | 时间同步,添加服务器时间头 |
|
|
370
|
-
| TokenInterceptor | 2 | Token 加密,添加认证头 |
|
|
371
|
-
| AuthInterceptor | 1 | 401 响应处理,跳转登录 |
|
|
305
|
+
## 工作流程
|
|
372
306
|
|
|
373
|
-
|
|
307
|
+
### Token 加密流程
|
|
374
308
|
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
console.log('请求超时');
|
|
386
|
-
} else if (error instanceof NetworkError) {
|
|
387
|
-
console.log('网络错误');
|
|
388
|
-
} else if (error instanceof HttpError) {
|
|
389
|
-
console.log('HTTP 错误:', error.status);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
309
|
+
```
|
|
310
|
+
请求发起时:
|
|
311
|
+
┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
|
312
|
+
│ getToken() │────>│ EncryptionService│────>│ 请求头 │
|
|
313
|
+
│ 获取 Token │ │ AES(token|+|time)│ │ Authorization │
|
|
314
|
+
└─────────────┘ └──────────────────┘ └─────────────────┘
|
|
315
|
+
│
|
|
316
|
+
▼
|
|
317
|
+
TimeSyncService
|
|
318
|
+
(服务器时间校正)
|
|
392
319
|
```
|
|
393
320
|
|
|
394
|
-
|
|
321
|
+
### 时间同步流程
|
|
395
322
|
|
|
396
|
-
|
|
323
|
+
```
|
|
324
|
+
1. 请求发起前检查本地时间是否需要同步
|
|
325
|
+
2. 如果超过同步间隔阈值(默认 50 秒),发起同步请求
|
|
326
|
+
3. 将服务器时间和客户端时间存储到 localStorage
|
|
327
|
+
4. 后续加密时使用校正后的时间戳
|
|
328
|
+
```
|
|
397
329
|
|
|
398
|
-
|
|
399
|
-
# Vite
|
|
400
|
-
VITE_BELINK_ENCRYPTION_KEY=your-key
|
|
401
|
-
VITE_BELINK_ENCRYPTION_IV=your-iv
|
|
330
|
+
#### 并发控制
|
|
402
331
|
|
|
403
|
-
|
|
404
|
-
BELINK_ENCRYPTION_KEY=your-key
|
|
405
|
-
BELINK_ENCRYPTION_IV=your-iv
|
|
406
|
-
```
|
|
332
|
+
当多个请求同时发起时,时间同步使用 Promise 锁机制,确保只发起一次同步请求:
|
|
407
333
|
|
|
408
|
-
|
|
334
|
+
```
|
|
335
|
+
请求1 ──┬── ensureSync() ──> 发起同步 ──> 等待完成 ──> 继续请求
|
|
336
|
+
请求2 ──┤ ↓
|
|
337
|
+
请求3 ──┼── ensureSync() ──> 检测到锁 ──> 等待同一个 Promise ──> 继续请求
|
|
338
|
+
请求4 ──┤
|
|
339
|
+
请求5 ──┘
|
|
340
|
+
```
|
|
409
341
|
|
|
410
|
-
|
|
411
|
-
// 请求相关
|
|
412
|
-
export type { RequestConfig, RequestContext, IConfig, HttpMethod } from '@be-link/request';
|
|
342
|
+
#### 防止系统时间篡改
|
|
413
343
|
|
|
414
|
-
|
|
415
|
-
export type { ApiResponse, ResponseContext, IApiResponse } from '@be-link/request';
|
|
344
|
+
使用 `performance.now()` 替代 `Date.now()` 计算时间流逝:
|
|
416
345
|
|
|
417
|
-
|
|
418
|
-
|
|
346
|
+
- `performance.now()` 返回页面加载后的高精度时间,不受系统时间修改影响
|
|
347
|
+
- 即使用户修改系统时间,Token 加密的时间戳仍然准确
|
|
419
348
|
|
|
420
|
-
|
|
421
|
-
export type { AppConfig, EncryptionConfig, TimeSyncConfig, CloudbaseConfig } from '@be-link/request';
|
|
349
|
+
## 类型导出
|
|
422
350
|
|
|
423
|
-
|
|
424
|
-
|
|
351
|
+
```typescript
|
|
352
|
+
// 单例实例
|
|
353
|
+
import { beLinkRequest } from '@be-link/request';
|
|
425
354
|
|
|
426
|
-
//
|
|
427
|
-
|
|
355
|
+
// 类(用于多实例)
|
|
356
|
+
import { BeLinkRequest } from '@be-link/request';
|
|
428
357
|
|
|
429
|
-
//
|
|
430
|
-
|
|
358
|
+
// 服务类
|
|
359
|
+
import { TimeSyncService, EncryptionService } from '@be-link/request';
|
|
431
360
|
|
|
432
|
-
//
|
|
433
|
-
|
|
361
|
+
// 配置类型
|
|
362
|
+
import type { RequestOptions, TimeSyncConfig, EncryptionConfig, ApiResponse } from '@be-link/request';
|
|
434
363
|
```
|
|
435
364
|
|
|
436
365
|
## 完整示例
|
|
437
366
|
|
|
438
367
|
```typescript
|
|
439
|
-
import { beLinkRequest
|
|
368
|
+
import { beLinkRequest } from '@be-link/request';
|
|
440
369
|
|
|
441
|
-
//
|
|
370
|
+
// 应用入口初始化
|
|
442
371
|
beLinkRequest.init({
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
372
|
+
baseURL: import.meta.env.VITE_API_URL,
|
|
373
|
+
timeout: 30000,
|
|
374
|
+
|
|
375
|
+
// 时间同步
|
|
376
|
+
timeSync: {
|
|
377
|
+
syncUrl: `${import.meta.env.VITE_API_URL}/api/time`,
|
|
447
378
|
},
|
|
448
|
-
tokenStorageKey: 'app_token',
|
|
449
|
-
});
|
|
450
379
|
|
|
451
|
-
//
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
380
|
+
// Token 加密
|
|
381
|
+
encryption: {
|
|
382
|
+
key: import.meta.env.VITE_ENCRYPTION_KEY,
|
|
383
|
+
iv: import.meta.env.VITE_ENCRYPTION_IV,
|
|
384
|
+
},
|
|
455
385
|
|
|
456
|
-
//
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
386
|
+
// Token 获取
|
|
387
|
+
getToken: () => localStorage.getItem('token'),
|
|
388
|
+
|
|
389
|
+
// 用户 ID
|
|
390
|
+
getUserId: () => {
|
|
391
|
+
const user = JSON.parse(localStorage.getItem('user') || '{}');
|
|
392
|
+
return user.id;
|
|
393
|
+
},
|
|
461
394
|
|
|
462
|
-
//
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
try {
|
|
468
|
-
const response = await beLinkRequest.get<{ name: string; age: number }>(`/api/users/${userId}`);
|
|
469
|
-
return response.data;
|
|
470
|
-
} catch (error) {
|
|
471
|
-
if (error instanceof AuthError) {
|
|
472
|
-
// 自动跳转登录页
|
|
473
|
-
return null;
|
|
395
|
+
// 错误处理
|
|
396
|
+
onError: (error) => {
|
|
397
|
+
if (error.response?.status === 401) {
|
|
398
|
+
localStorage.removeItem('token');
|
|
399
|
+
window.location.href = '/login';
|
|
474
400
|
}
|
|
475
401
|
throw error;
|
|
476
|
-
}
|
|
402
|
+
},
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
// API 调用
|
|
406
|
+
async function getUserInfo(userId: string) {
|
|
407
|
+
return beLinkRequest.get(`/api/users/${userId}`);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
async function createUser(data: { name: string; age: number }) {
|
|
411
|
+
return beLinkRequest.post('/api/users', data);
|
|
477
412
|
}
|
|
478
413
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
const response = await beLinkLiveRequest.get(`/api/room/${roomId}`);
|
|
482
|
-
return response.data;
|
|
414
|
+
async function updateUser(userId: string, data: { name?: string; age?: number }) {
|
|
415
|
+
return beLinkRequest.put(`/api/users/${userId}`, data);
|
|
483
416
|
}
|
|
484
417
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
const result = await callFunction({
|
|
488
|
-
name: 'order-service',
|
|
489
|
-
data: {
|
|
490
|
-
moduleName: 'order',
|
|
491
|
-
serviceName: 'main',
|
|
492
|
-
funcName: 'create',
|
|
493
|
-
param: { productId },
|
|
494
|
-
},
|
|
495
|
-
});
|
|
496
|
-
return result;
|
|
418
|
+
async function deleteUser(userId: string) {
|
|
419
|
+
return beLinkRequest.delete(`/api/users/${userId}`);
|
|
497
420
|
}
|
|
498
421
|
```
|
|
499
422
|
|