@ahoo-wang/fetcher 0.3.6 → 0.5.0
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 +91 -188
- package/README.zh-CN.md +87 -183
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,83 +6,63 @@
|
|
|
6
6
|
[](https://github.com/Ahoo-Wang/fetcher/blob/main/LICENSE)
|
|
7
7
|
[](https://www.npmjs.com/package/@ahoo-wang/fetcher)
|
|
8
8
|
|
|
9
|
-
A modern HTTP client library based on the Fetch API, designed to simplify and optimize interactions with
|
|
9
|
+
A modern, lightweight HTTP client library based on the Fetch API, designed to simplify and optimize interactions with
|
|
10
|
+
backend RESTful APIs. It provides an Axios-like API with support for path parameters, query parameters, timeout
|
|
11
|
+
settings, and request/response interceptors.
|
|
10
12
|
|
|
11
|
-
## Features
|
|
13
|
+
## 🌟 Features
|
|
12
14
|
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
15
|
+
- **🔄 Fetch API Compatible**: Fully compatible with the native Fetch API for easy adoption
|
|
16
|
+
- **🧭 Path & Query Parameters**: Native support for path parameters (`{id}`) and query parameters
|
|
17
|
+
- **⏱️ Timeout Control**: Configurable request timeouts with proper error handling
|
|
18
|
+
- **🔗 Interceptor System**: Request, response, and error interceptors for middleware patterns
|
|
19
|
+
- **🎯 Automatic Body Conversion**: Converts JavaScript objects to JSON with proper headers
|
|
20
|
+
- **🛡️ TypeScript Support**: Complete TypeScript definitions for type-safe development
|
|
21
|
+
- **🧩 Modular Architecture**: Lightweight core with optional extension packages
|
|
22
|
+
- **📦 Named Fetcher Support**: Automatic registration and retrieval of fetcher instances
|
|
23
|
+
- **⚙️ Default Fetcher**: Pre-configured default fetcher instance for quick start
|
|
22
24
|
|
|
23
|
-
##
|
|
25
|
+
## 🚀 Quick Start
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
pnpm add @ahoo-wang/fetcher
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
Using npm:
|
|
27
|
+
### Installation
|
|
32
28
|
|
|
33
29
|
```bash
|
|
30
|
+
# Using npm
|
|
34
31
|
npm install @ahoo-wang/fetcher
|
|
35
|
-
```
|
|
36
32
|
|
|
37
|
-
Using
|
|
33
|
+
# Using pnpm
|
|
34
|
+
pnpm add @ahoo-wang/fetcher
|
|
38
35
|
|
|
39
|
-
|
|
36
|
+
# Using yarn
|
|
40
37
|
yarn add @ahoo-wang/fetcher
|
|
41
38
|
```
|
|
42
39
|
|
|
43
|
-
## Usage
|
|
44
|
-
|
|
45
40
|
### Basic Usage
|
|
46
41
|
|
|
47
42
|
```typescript
|
|
48
43
|
import { Fetcher } from '@ahoo-wang/fetcher';
|
|
49
44
|
|
|
45
|
+
// Create a fetcher instance
|
|
50
46
|
const fetcher = new Fetcher({
|
|
51
47
|
baseURL: 'https://api.example.com',
|
|
52
48
|
timeout: 5000,
|
|
53
49
|
});
|
|
54
50
|
|
|
55
|
-
// GET request with path
|
|
56
|
-
fetcher
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
.then(response => {
|
|
62
|
-
console.log(response.data);
|
|
63
|
-
})
|
|
64
|
-
.catch(error => {
|
|
65
|
-
console.error(error);
|
|
66
|
-
});
|
|
51
|
+
// GET request with path and query parameters
|
|
52
|
+
const response = await fetcher.get('/users/{id}', {
|
|
53
|
+
pathParams: { id: 123 },
|
|
54
|
+
queryParams: { include: 'profile' },
|
|
55
|
+
});
|
|
56
|
+
const userData = await response.json();
|
|
67
57
|
|
|
68
|
-
// POST request with
|
|
69
|
-
fetcher
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
})
|
|
73
|
-
.then(response => {
|
|
74
|
-
console.log(response.data);
|
|
75
|
-
})
|
|
76
|
-
.catch(error => {
|
|
77
|
-
console.error(error);
|
|
78
|
-
});
|
|
58
|
+
// POST request with automatic JSON conversion
|
|
59
|
+
const createUserResponse = await fetcher.post('/users', {
|
|
60
|
+
body: { name: 'John Doe', email: 'john@example.com' },
|
|
61
|
+
});
|
|
79
62
|
```
|
|
80
63
|
|
|
81
64
|
### Named Fetcher Usage
|
|
82
65
|
|
|
83
|
-
NamedFetcher is an extension of the Fetcher class that automatically registers itself with a global registrar. This is
|
|
84
|
-
useful when you need to manage multiple fetcher instances throughout your application.
|
|
85
|
-
|
|
86
66
|
```typescript
|
|
87
67
|
import { NamedFetcher, fetcherRegistrar } from '@ahoo-wang/fetcher';
|
|
88
68
|
|
|
@@ -96,30 +76,16 @@ const apiFetcher = new NamedFetcher('api', {
|
|
|
96
76
|
},
|
|
97
77
|
});
|
|
98
78
|
|
|
99
|
-
// Create another named fetcher for a different service
|
|
100
|
-
const authFetcher = new NamedFetcher('auth', {
|
|
101
|
-
baseURL: 'https://auth.example.com',
|
|
102
|
-
timeout: 3000,
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
// Use the fetcher normally
|
|
106
|
-
apiFetcher
|
|
107
|
-
.get('/users/123')
|
|
108
|
-
.then(response => response.json())
|
|
109
|
-
.then(data => console.log(data));
|
|
110
|
-
|
|
111
79
|
// Retrieve a named fetcher from the registrar
|
|
112
80
|
const retrievedFetcher = fetcherRegistrar.get('api');
|
|
113
81
|
if (retrievedFetcher) {
|
|
114
|
-
retrievedFetcher.
|
|
115
|
-
body: { name: 'Jane Doe' },
|
|
116
|
-
});
|
|
82
|
+
const response = await retrievedFetcher.get('/users/123');
|
|
117
83
|
}
|
|
118
84
|
|
|
119
85
|
// Use requiredGet to retrieve a fetcher (throws error if not found)
|
|
120
86
|
try {
|
|
121
87
|
const authFetcher = fetcherRegistrar.requiredGet('auth');
|
|
122
|
-
authFetcher.post('/login', {
|
|
88
|
+
await authFetcher.post('/login', {
|
|
123
89
|
body: { username: 'user', password: 'pass' },
|
|
124
90
|
});
|
|
125
91
|
} catch (error) {
|
|
@@ -129,68 +95,72 @@ try {
|
|
|
129
95
|
|
|
130
96
|
### Default Fetcher Usage
|
|
131
97
|
|
|
132
|
-
The library also exports a pre-configured default fetcher instance that can be used directly:
|
|
133
|
-
|
|
134
98
|
```typescript
|
|
135
99
|
import { fetcher } from '@ahoo-wang/fetcher';
|
|
136
100
|
|
|
137
101
|
// Use the default fetcher directly
|
|
138
|
-
fetcher
|
|
139
|
-
|
|
140
|
-
.then(response => response.json())
|
|
141
|
-
.then(data => console.log(data));
|
|
142
|
-
|
|
143
|
-
// The default fetcher is also available through the registrar
|
|
144
|
-
import { fetcherRegistrar } from '@ahoo-wang/fetcher';
|
|
145
|
-
|
|
146
|
-
const defaultFetcher = fetcherRegistrar.default;
|
|
147
|
-
// defaultFetcher is the same instance as fetcher
|
|
148
|
-
console.log(defaultFetcher === fetcher); // true
|
|
102
|
+
const response = await fetcher.get('/users');
|
|
103
|
+
const data = await response.json();
|
|
149
104
|
```
|
|
150
105
|
|
|
151
|
-
|
|
106
|
+
## 🔗 Interceptor System
|
|
107
|
+
|
|
108
|
+
### Request Interceptors
|
|
152
109
|
|
|
153
110
|
```typescript
|
|
154
111
|
import { Fetcher } from '@ahoo-wang/fetcher';
|
|
155
112
|
|
|
156
113
|
const fetcher = new Fetcher({ baseURL: 'https://api.example.com' });
|
|
157
114
|
|
|
158
|
-
// Add request interceptor
|
|
159
|
-
const
|
|
115
|
+
// Add request interceptor (e.g., for authentication)
|
|
116
|
+
const interceptorId = fetcher.interceptors.request.use({
|
|
160
117
|
intercept(exchange) {
|
|
161
|
-
// Modify request configuration, e.g., add auth header
|
|
162
118
|
return {
|
|
163
119
|
...exchange,
|
|
164
120
|
request: {
|
|
165
121
|
...exchange.request,
|
|
166
122
|
headers: {
|
|
167
123
|
...exchange.request.headers,
|
|
168
|
-
Authorization: 'Bearer
|
|
124
|
+
Authorization: 'Bearer ' + getAuthToken(),
|
|
169
125
|
},
|
|
170
126
|
},
|
|
171
127
|
};
|
|
172
128
|
},
|
|
173
129
|
});
|
|
174
130
|
|
|
175
|
-
//
|
|
176
|
-
|
|
131
|
+
// Remove interceptor
|
|
132
|
+
fetcher.interceptors.request.eject(interceptorId);
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Response Interceptors
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
// Add response interceptor (e.g., for logging)
|
|
139
|
+
fetcher.interceptors.response.use({
|
|
177
140
|
intercept(exchange) {
|
|
178
|
-
|
|
141
|
+
console.log('Response received:', exchange.response.status);
|
|
179
142
|
return exchange;
|
|
180
143
|
},
|
|
181
144
|
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Error Interceptors
|
|
182
148
|
|
|
183
|
-
|
|
184
|
-
|
|
149
|
+
```typescript
|
|
150
|
+
// Add error interceptor (e.g., for unified error handling)
|
|
151
|
+
fetcher.interceptors.error.use({
|
|
185
152
|
intercept(exchange) {
|
|
186
|
-
|
|
187
|
-
|
|
153
|
+
if (exchange.error?.name === 'FetchTimeoutError') {
|
|
154
|
+
console.error('Request timeout:', exchange.error.message);
|
|
155
|
+
} else {
|
|
156
|
+
console.error('Network error:', exchange.error?.message);
|
|
157
|
+
}
|
|
188
158
|
return exchange;
|
|
189
159
|
},
|
|
190
160
|
});
|
|
191
161
|
```
|
|
192
162
|
|
|
193
|
-
## API Reference
|
|
163
|
+
## 📚 API Reference
|
|
194
164
|
|
|
195
165
|
### Fetcher Class
|
|
196
166
|
|
|
@@ -199,14 +169,14 @@ Core HTTP client class that provides various HTTP methods.
|
|
|
199
169
|
#### Constructor
|
|
200
170
|
|
|
201
171
|
```typescript
|
|
202
|
-
new Fetcher(options: FetcherOptions
|
|
172
|
+
new Fetcher(options ? : FetcherOptions);
|
|
203
173
|
```
|
|
204
174
|
|
|
205
|
-
**
|
|
175
|
+
**Options:**
|
|
206
176
|
|
|
207
|
-
- `
|
|
208
|
-
- `
|
|
209
|
-
- `
|
|
177
|
+
- `baseURL`: Base URL for all requests
|
|
178
|
+
- `timeout`: Request timeout in milliseconds
|
|
179
|
+
- `headers`: Default request headers
|
|
210
180
|
|
|
211
181
|
#### Methods
|
|
212
182
|
|
|
@@ -221,25 +191,18 @@ new Fetcher(options: FetcherOptions = defaultOptions)
|
|
|
221
191
|
|
|
222
192
|
### NamedFetcher Class
|
|
223
193
|
|
|
224
|
-
An extension of the Fetcher class that automatically registers itself with the global fetcherRegistrar
|
|
225
|
-
name.
|
|
194
|
+
An extension of the Fetcher class that automatically registers itself with the global fetcherRegistrar.
|
|
226
195
|
|
|
227
196
|
#### Constructor
|
|
228
197
|
|
|
229
198
|
```typescript
|
|
230
199
|
new NamedFetcher(name
|
|
231
200
|
:
|
|
232
|
-
string, options
|
|
233
|
-
:
|
|
234
|
-
FetcherOptions = defaultOptions
|
|
201
|
+
string, options ? : FetcherOptions
|
|
235
202
|
)
|
|
203
|
+
;
|
|
236
204
|
```
|
|
237
205
|
|
|
238
|
-
**Parameters:**
|
|
239
|
-
|
|
240
|
-
- `name`: The name to register this fetcher under
|
|
241
|
-
- `options`: Same as Fetcher constructor options
|
|
242
|
-
|
|
243
206
|
### FetcherRegistrar
|
|
244
207
|
|
|
245
208
|
Global instance for managing multiple Fetcher instances by name.
|
|
@@ -256,112 +219,52 @@ Global instance for managing multiple Fetcher instances by name.
|
|
|
256
219
|
- `requiredGet(name: string): Fetcher` - Get a fetcher by name, throws if not found
|
|
257
220
|
- `fetchers: Map<string, Fetcher>` - Get all registered fetchers
|
|
258
221
|
|
|
259
|
-
###
|
|
260
|
-
|
|
261
|
-
URL builder for constructing complete URLs with parameters.
|
|
222
|
+
### Interceptor System
|
|
262
223
|
|
|
263
|
-
####
|
|
264
|
-
|
|
265
|
-
- `build(path: string, pathParams?: Record<string, any>, queryParams?: Record<string, any>): string` - Build complete URL
|
|
266
|
-
|
|
267
|
-
### InterceptorManager Class
|
|
224
|
+
#### InterceptorManager
|
|
268
225
|
|
|
269
226
|
Interceptor manager for managing multiple interceptors of the same type.
|
|
270
227
|
|
|
271
|
-
|
|
228
|
+
**Methods:**
|
|
272
229
|
|
|
273
230
|
- `use(interceptor: Interceptor): number` - Add interceptor, returns interceptor ID
|
|
274
231
|
- `eject(index: number): void` - Remove interceptor by ID
|
|
275
232
|
- `clear(): void` - Clear all interceptors
|
|
276
233
|
- `intercept(exchange: FetchExchange): Promise<FetchExchange>` - Execute all interceptors sequentially
|
|
277
234
|
|
|
278
|
-
|
|
235
|
+
#### FetcherInterceptors
|
|
279
236
|
|
|
280
237
|
Fetcher interceptor collection, including request, response, and error interceptor managers.
|
|
281
238
|
|
|
282
|
-
|
|
239
|
+
**Properties:**
|
|
283
240
|
|
|
284
241
|
- `request: InterceptorManager` - Request interceptor manager
|
|
285
242
|
- `response: InterceptorManager` - Response interceptor manager
|
|
286
243
|
- `error: InterceptorManager` - Error interceptor manager
|
|
287
244
|
|
|
288
|
-
##
|
|
289
|
-
|
|
290
|
-
```typescript
|
|
291
|
-
import { Fetcher } from '@ahoo-wang/fetcher';
|
|
292
|
-
|
|
293
|
-
// Create fetcher instance
|
|
294
|
-
const fetcher = new Fetcher({
|
|
295
|
-
baseURL: 'https://api.example.com',
|
|
296
|
-
timeout: 10000,
|
|
297
|
-
headers: {
|
|
298
|
-
'Content-Type': 'application/json',
|
|
299
|
-
},
|
|
300
|
-
});
|
|
245
|
+
## 🛠️ Development
|
|
301
246
|
|
|
302
|
-
|
|
303
|
-
fetcher.interceptors.request.use({
|
|
304
|
-
intercept(exchange) {
|
|
305
|
-
return {
|
|
306
|
-
...exchange,
|
|
307
|
-
request: {
|
|
308
|
-
...exchange.request,
|
|
309
|
-
headers: {
|
|
310
|
-
...exchange.request.headers,
|
|
311
|
-
Authorization: 'Bearer ' + getAuthToken(),
|
|
312
|
-
},
|
|
313
|
-
},
|
|
314
|
-
};
|
|
315
|
-
},
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
// Add response interceptor - Process response
|
|
319
|
-
fetcher.interceptors.response.use({
|
|
320
|
-
intercept(exchange) {
|
|
321
|
-
// Note: Response processing would typically happen after the response is received
|
|
322
|
-
return exchange;
|
|
323
|
-
},
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
// Add error interceptor - Unified error handling
|
|
327
|
-
fetcher.interceptors.error.use({
|
|
328
|
-
intercept(exchange) {
|
|
329
|
-
if (exchange.error?.name === 'FetchTimeoutError') {
|
|
330
|
-
console.error('Request timeout:', exchange.error.message);
|
|
331
|
-
} else {
|
|
332
|
-
console.error('Network error:', exchange.error?.message);
|
|
333
|
-
}
|
|
334
|
-
return exchange;
|
|
335
|
-
},
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
// Use fetcher to make requests
|
|
339
|
-
fetcher
|
|
340
|
-
.get('/users/{id}', {
|
|
341
|
-
pathParams: { id: 123 },
|
|
342
|
-
queryParams: { include: 'profile,posts' },
|
|
343
|
-
})
|
|
344
|
-
.then(response => response.json())
|
|
345
|
-
.then(data => {
|
|
346
|
-
console.log('User data:', data);
|
|
347
|
-
})
|
|
348
|
-
.catch(error => {
|
|
349
|
-
console.error('Failed to fetch user:', error);
|
|
350
|
-
});
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
## Testing
|
|
354
|
-
|
|
355
|
-
Run tests:
|
|
247
|
+
### Testing
|
|
356
248
|
|
|
357
249
|
```bash
|
|
250
|
+
# Run tests
|
|
358
251
|
pnpm test
|
|
252
|
+
|
|
253
|
+
# Run tests with coverage
|
|
254
|
+
pnpm test --coverage
|
|
359
255
|
```
|
|
360
256
|
|
|
361
|
-
## Contributing
|
|
257
|
+
## 🤝 Contributing
|
|
362
258
|
|
|
363
|
-
Contributions
|
|
259
|
+
Contributions are welcome! Please see
|
|
260
|
+
the [contributing guide](https://github.com/Ahoo-Wang/fetcher/blob/main/CONTRIBUTING.md) for more details.
|
|
364
261
|
|
|
365
|
-
## License
|
|
262
|
+
## 📄 License
|
|
366
263
|
|
|
367
264
|
This project is licensed under the [Apache-2.0 License](https://opensource.org/licenses/Apache-2.0).
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
<p align="center">
|
|
269
|
+
Part of the <a href="https://github.com/Ahoo-Wang/fetcher">Fetcher</a> ecosystem
|
|
270
|
+
</p>
|
package/README.zh-CN.md
CHANGED
|
@@ -6,83 +6,62 @@
|
|
|
6
6
|
[](https://github.com/Ahoo-Wang/fetcher/blob/main/LICENSE)
|
|
7
7
|
[](https://www.npmjs.com/package/@ahoo-wang/fetcher)
|
|
8
8
|
|
|
9
|
-
一个基于 Fetch API
|
|
9
|
+
一个基于 Fetch API 的现代、轻量级 HTTP 客户端库,旨在简化和优化与后端 RESTful API 的交互。它提供了类似 Axios 的
|
|
10
|
+
API,支持路径参数、查询参数、超时设置和请求/响应拦截器。
|
|
10
11
|
|
|
11
|
-
## 特性
|
|
12
|
+
## 🌟 特性
|
|
12
13
|
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
14
|
+
- **🔄 Fetch API 兼容**:与原生 Fetch API 完全兼容,易于上手
|
|
15
|
+
- **🧭 路径和查询参数**:原生支持路径参数(`{id}`)和查询参数
|
|
16
|
+
- **⏱️ 超时控制**:可配置的请求超时和适当的错误处理
|
|
17
|
+
- **🔗 拦截器系统**:请求、响应和错误拦截器的中间件模式
|
|
18
|
+
- **🎯 自动请求体转换**:自动将 JavaScript 对象转换为 JSON 并设置适当头部
|
|
19
|
+
- **🛡️ TypeScript 支持**:完整的 TypeScript 类型定义,提升开发体验
|
|
20
|
+
- **🧩 模块化架构**:轻量级核心和可选的扩展包
|
|
21
|
+
- **📦 命名 Fetcher 支持**:自动注册和检索 fetcher 实例
|
|
22
|
+
- **⚙️ 默认 Fetcher**:预配置的默认 fetcher 实例,快速开始
|
|
22
23
|
|
|
23
|
-
##
|
|
24
|
+
## 🚀 快速开始
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
pnpm add @ahoo-wang/fetcher
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
使用 npm:
|
|
26
|
+
### 安装
|
|
32
27
|
|
|
33
28
|
```bash
|
|
29
|
+
# 使用 npm
|
|
34
30
|
npm install @ahoo-wang/fetcher
|
|
35
|
-
```
|
|
36
31
|
|
|
37
|
-
使用
|
|
32
|
+
# 使用 pnpm
|
|
33
|
+
pnpm add @ahoo-wang/fetcher
|
|
38
34
|
|
|
39
|
-
|
|
35
|
+
# 使用 yarn
|
|
40
36
|
yarn add @ahoo-wang/fetcher
|
|
41
37
|
```
|
|
42
38
|
|
|
43
|
-
## 使用
|
|
44
|
-
|
|
45
39
|
### 基本用法
|
|
46
40
|
|
|
47
41
|
```typescript
|
|
48
42
|
import { Fetcher } from '@ahoo-wang/fetcher';
|
|
49
43
|
|
|
44
|
+
// 创建 fetcher 实例
|
|
50
45
|
const fetcher = new Fetcher({
|
|
51
46
|
baseURL: 'https://api.example.com',
|
|
52
47
|
timeout: 5000,
|
|
53
48
|
});
|
|
54
49
|
|
|
55
|
-
//
|
|
56
|
-
fetcher
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
.then(response => {
|
|
62
|
-
console.log(response.data);
|
|
63
|
-
})
|
|
64
|
-
.catch(error => {
|
|
65
|
-
console.error(error);
|
|
66
|
-
});
|
|
50
|
+
// 带路径和查询参数的 GET 请求
|
|
51
|
+
const response = await fetcher.get('/users/{id}', {
|
|
52
|
+
pathParams: { id: 123 },
|
|
53
|
+
queryParams: { include: 'profile' },
|
|
54
|
+
});
|
|
55
|
+
const userData = await response.json();
|
|
67
56
|
|
|
68
|
-
//
|
|
69
|
-
fetcher
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
})
|
|
73
|
-
.then(response => {
|
|
74
|
-
console.log(response.data);
|
|
75
|
-
})
|
|
76
|
-
.catch(error => {
|
|
77
|
-
console.error(error);
|
|
78
|
-
});
|
|
57
|
+
// 带自动 JSON 转换的 POST 请求
|
|
58
|
+
const createUserResponse = await fetcher.post('/users', {
|
|
59
|
+
body: { name: 'John Doe', email: 'john@example.com' },
|
|
60
|
+
});
|
|
79
61
|
```
|
|
80
62
|
|
|
81
63
|
### 命名 Fetcher 用法
|
|
82
64
|
|
|
83
|
-
NamedFetcher 是 Fetcher 类的扩展,它会自动使用提供的名称在全局注册器中注册自己。当您需要在应用程序中管理多个 fetcher
|
|
84
|
-
实例时,这很有用。
|
|
85
|
-
|
|
86
65
|
```typescript
|
|
87
66
|
import { NamedFetcher, fetcherRegistrar } from '@ahoo-wang/fetcher';
|
|
88
67
|
|
|
@@ -96,30 +75,16 @@ const apiFetcher = new NamedFetcher('api', {
|
|
|
96
75
|
},
|
|
97
76
|
});
|
|
98
77
|
|
|
99
|
-
// 为不同的服务创建另一个命名 fetcher
|
|
100
|
-
const authFetcher = new NamedFetcher('auth', {
|
|
101
|
-
baseURL: 'https://auth.example.com',
|
|
102
|
-
timeout: 3000,
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
// 正常使用 fetcher
|
|
106
|
-
apiFetcher
|
|
107
|
-
.get('/users/123')
|
|
108
|
-
.then(response => response.json())
|
|
109
|
-
.then(data => console.log(data));
|
|
110
|
-
|
|
111
78
|
// 从注册器中检索命名 fetcher
|
|
112
79
|
const retrievedFetcher = fetcherRegistrar.get('api');
|
|
113
80
|
if (retrievedFetcher) {
|
|
114
|
-
retrievedFetcher.
|
|
115
|
-
body: { name: 'Jane Doe' },
|
|
116
|
-
});
|
|
81
|
+
const response = await retrievedFetcher.get('/users/123');
|
|
117
82
|
}
|
|
118
83
|
|
|
119
84
|
// 使用 requiredGet 检索 fetcher(如果未找到则抛出错误)
|
|
120
85
|
try {
|
|
121
86
|
const authFetcher = fetcherRegistrar.requiredGet('auth');
|
|
122
|
-
authFetcher.post('/login', {
|
|
87
|
+
await authFetcher.post('/login', {
|
|
123
88
|
body: { username: 'user', password: 'pass' },
|
|
124
89
|
});
|
|
125
90
|
} catch (error) {
|
|
@@ -129,68 +94,72 @@ try {
|
|
|
129
94
|
|
|
130
95
|
### 默认 Fetcher 用法
|
|
131
96
|
|
|
132
|
-
该库还导出了一个预配置的默认 fetcher 实例,可以直接使用:
|
|
133
|
-
|
|
134
97
|
```typescript
|
|
135
98
|
import { fetcher } from '@ahoo-wang/fetcher';
|
|
136
99
|
|
|
137
100
|
// 直接使用默认 fetcher
|
|
138
|
-
fetcher
|
|
139
|
-
|
|
140
|
-
.then(response => response.json())
|
|
141
|
-
.then(data => console.log(data));
|
|
142
|
-
|
|
143
|
-
// 默认 fetcher 也可以通过注册器获取
|
|
144
|
-
import { fetcherRegistrar } from '@ahoo-wang/fetcher';
|
|
145
|
-
|
|
146
|
-
const defaultFetcher = fetcherRegistrar.default;
|
|
147
|
-
// defaultFetcher 与 fetcher 是同一个实例
|
|
148
|
-
console.log(defaultFetcher === fetcher); // true
|
|
101
|
+
const response = await fetcher.get('/users');
|
|
102
|
+
const data = await response.json();
|
|
149
103
|
```
|
|
150
104
|
|
|
151
|
-
|
|
105
|
+
## 🔗 拦截器系统
|
|
106
|
+
|
|
107
|
+
### 请求拦截器
|
|
152
108
|
|
|
153
109
|
```typescript
|
|
154
110
|
import { Fetcher } from '@ahoo-wang/fetcher';
|
|
155
111
|
|
|
156
112
|
const fetcher = new Fetcher({ baseURL: 'https://api.example.com' });
|
|
157
113
|
|
|
158
|
-
//
|
|
159
|
-
const
|
|
114
|
+
// 添加请求拦截器(例如用于认证)
|
|
115
|
+
const interceptorId = fetcher.interceptors.request.use({
|
|
160
116
|
intercept(exchange) {
|
|
161
|
-
// 修改请求配置,例如添加认证头部
|
|
162
117
|
return {
|
|
163
118
|
...exchange,
|
|
164
119
|
request: {
|
|
165
120
|
...exchange.request,
|
|
166
121
|
headers: {
|
|
167
122
|
...exchange.request.headers,
|
|
168
|
-
Authorization: 'Bearer
|
|
123
|
+
Authorization: 'Bearer ' + getAuthToken(),
|
|
169
124
|
},
|
|
170
125
|
},
|
|
171
126
|
};
|
|
172
127
|
},
|
|
173
128
|
});
|
|
174
129
|
|
|
175
|
-
//
|
|
176
|
-
|
|
130
|
+
// 移除拦截器
|
|
131
|
+
fetcher.interceptors.request.eject(interceptorId);
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 响应拦截器
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
// 添加响应拦截器(例如用于日志记录)
|
|
138
|
+
fetcher.interceptors.response.use({
|
|
177
139
|
intercept(exchange) {
|
|
178
|
-
|
|
140
|
+
console.log('收到响应:', exchange.response.status);
|
|
179
141
|
return exchange;
|
|
180
142
|
},
|
|
181
143
|
});
|
|
144
|
+
```
|
|
182
145
|
|
|
183
|
-
|
|
184
|
-
|
|
146
|
+
### 错误拦截器
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
// 添加错误拦截器(例如用于统一错误处理)
|
|
150
|
+
fetcher.interceptors.error.use({
|
|
185
151
|
intercept(exchange) {
|
|
186
|
-
|
|
187
|
-
|
|
152
|
+
if (exchange.error?.name === 'FetchTimeoutError') {
|
|
153
|
+
console.error('请求超时:', exchange.error.message);
|
|
154
|
+
} else {
|
|
155
|
+
console.error('网络错误:', exchange.error?.message);
|
|
156
|
+
}
|
|
188
157
|
return exchange;
|
|
189
158
|
},
|
|
190
159
|
});
|
|
191
160
|
```
|
|
192
161
|
|
|
193
|
-
## API 参考
|
|
162
|
+
## 📚 API 参考
|
|
194
163
|
|
|
195
164
|
### Fetcher 类
|
|
196
165
|
|
|
@@ -199,14 +168,14 @@ const errorInterceptorId = fetcher.interceptors.error.use({
|
|
|
199
168
|
#### 构造函数
|
|
200
169
|
|
|
201
170
|
```typescript
|
|
202
|
-
new Fetcher(options
|
|
171
|
+
new Fetcher(options ? : FetcherOptions);
|
|
203
172
|
```
|
|
204
173
|
|
|
205
|
-
|
|
174
|
+
**选项:**
|
|
206
175
|
|
|
207
|
-
- `
|
|
208
|
-
- `
|
|
209
|
-
- `
|
|
176
|
+
- `baseURL`:基础 URL
|
|
177
|
+
- `timeout`:以毫秒为单位的请求超时
|
|
178
|
+
- `headers`:默认请求头部
|
|
210
179
|
|
|
211
180
|
#### 方法
|
|
212
181
|
|
|
@@ -230,13 +199,9 @@ new NamedFetcher(name
|
|
|
230
199
|
:
|
|
231
200
|
string, options ? : FetcherOptions
|
|
232
201
|
)
|
|
202
|
+
;
|
|
233
203
|
```
|
|
234
204
|
|
|
235
|
-
**参数:**
|
|
236
|
-
|
|
237
|
-
- `name`:注册此 fetcher 的名称
|
|
238
|
-
- `options`:与 Fetcher 构造函数相同的选项
|
|
239
|
-
|
|
240
205
|
### FetcherRegistrar
|
|
241
206
|
|
|
242
207
|
用于按名称管理多个 Fetcher 实例的全局实例。
|
|
@@ -253,112 +218,51 @@ string, options ? : FetcherOptions
|
|
|
253
218
|
- `requiredGet(name: string): Fetcher` - 按名称获取 fetcher,如果未找到则抛出错误
|
|
254
219
|
- `fetchers: Map<string, Fetcher>` - 获取所有已注册的 fetcher
|
|
255
220
|
|
|
256
|
-
###
|
|
257
|
-
|
|
258
|
-
用于构建带参数的完整 URL 的 URL 构建器。
|
|
259
|
-
|
|
260
|
-
#### 方法
|
|
261
|
-
|
|
262
|
-
- `build(path: string, pathParams?: Record<string, any>, queryParams?: Record<string, any>): string` - 构建完整 URL
|
|
221
|
+
### 拦截器系统
|
|
263
222
|
|
|
264
|
-
|
|
223
|
+
#### InterceptorManager
|
|
265
224
|
|
|
266
225
|
用于管理同一类型多个拦截器的拦截器管理器。
|
|
267
226
|
|
|
268
|
-
|
|
227
|
+
**方法:**
|
|
269
228
|
|
|
270
229
|
- `use(interceptor: Interceptor): number` - 添加拦截器,返回拦截器 ID
|
|
271
230
|
- `eject(index: number): void` - 按 ID 移除拦截器
|
|
272
231
|
- `clear(): void` - 清除所有拦截器
|
|
273
232
|
- `intercept(exchange: FetchExchange): Promise<FetchExchange>` - 顺序执行所有拦截器
|
|
274
233
|
|
|
275
|
-
|
|
234
|
+
#### FetcherInterceptors
|
|
276
235
|
|
|
277
236
|
Fetcher 拦截器集合,包括请求、响应和错误拦截器管理器。
|
|
278
237
|
|
|
279
|
-
|
|
238
|
+
**属性:**
|
|
280
239
|
|
|
281
240
|
- `request: InterceptorManager` - 请求拦截器管理器
|
|
282
241
|
- `response: InterceptorManager` - 响应拦截器管理器
|
|
283
242
|
- `error: InterceptorManager` - 错误拦截器管理器
|
|
284
243
|
|
|
285
|
-
##
|
|
244
|
+
## 🛠️ 开发
|
|
286
245
|
|
|
287
|
-
|
|
288
|
-
import { Fetcher } from '@ahoo-wang/fetcher';
|
|
289
|
-
|
|
290
|
-
// 创建 fetcher 实例
|
|
291
|
-
const fetcher = new Fetcher({
|
|
292
|
-
baseURL: 'https://api.example.com',
|
|
293
|
-
timeout: 10000,
|
|
294
|
-
headers: {
|
|
295
|
-
'Content-Type': 'application/json',
|
|
296
|
-
},
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
// 添加请求拦截器 - 添加认证头部
|
|
300
|
-
fetcher.interceptors.request.use({
|
|
301
|
-
intercept(exchange) {
|
|
302
|
-
return {
|
|
303
|
-
...exchange,
|
|
304
|
-
request: {
|
|
305
|
-
...exchange.request,
|
|
306
|
-
headers: {
|
|
307
|
-
...exchange.request.headers,
|
|
308
|
-
Authorization: 'Bearer ' + getAuthToken(),
|
|
309
|
-
},
|
|
310
|
-
},
|
|
311
|
-
};
|
|
312
|
-
},
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
// 添加响应拦截器 - 处理响应
|
|
316
|
-
fetcher.interceptors.response.use({
|
|
317
|
-
intercept(exchange) {
|
|
318
|
-
// 注意:响应处理通常在收到响应后进行
|
|
319
|
-
return exchange;
|
|
320
|
-
},
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
// 添加错误拦截器 - 统一错误处理
|
|
324
|
-
fetcher.interceptors.error.use({
|
|
325
|
-
intercept(exchange) {
|
|
326
|
-
if (exchange.error?.name === 'FetchTimeoutError') {
|
|
327
|
-
console.error('请求超时:', exchange.error.message);
|
|
328
|
-
} else {
|
|
329
|
-
console.error('网络错误:', exchange.error?.message);
|
|
330
|
-
}
|
|
331
|
-
return exchange;
|
|
332
|
-
},
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
// 使用 fetcher 发起请求
|
|
336
|
-
fetcher
|
|
337
|
-
.get('/users/{id}', {
|
|
338
|
-
pathParams: { id: 123 },
|
|
339
|
-
queryParams: { include: 'profile,posts' },
|
|
340
|
-
})
|
|
341
|
-
.then(response => response.json())
|
|
342
|
-
.then(data => {
|
|
343
|
-
console.log('用户数据:', data);
|
|
344
|
-
})
|
|
345
|
-
.catch(error => {
|
|
346
|
-
console.error('获取用户失败:', error);
|
|
347
|
-
});
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
## 测试
|
|
351
|
-
|
|
352
|
-
运行测试:
|
|
246
|
+
### 测试
|
|
353
247
|
|
|
354
248
|
```bash
|
|
249
|
+
# 运行测试
|
|
355
250
|
pnpm test
|
|
251
|
+
|
|
252
|
+
# 运行带覆盖率的测试
|
|
253
|
+
pnpm test --coverage
|
|
356
254
|
```
|
|
357
255
|
|
|
358
|
-
## 贡献
|
|
256
|
+
## 🤝 贡献
|
|
359
257
|
|
|
360
|
-
|
|
258
|
+
欢迎贡献!请查看 [贡献指南](https://github.com/Ahoo-Wang/fetcher/blob/main/CONTRIBUTING.md) 了解更多详情。
|
|
361
259
|
|
|
362
|
-
## 许可证
|
|
260
|
+
## 📄 许可证
|
|
363
261
|
|
|
364
262
|
本项目采用 [Apache-2.0 许可证](https://opensource.org/licenses/Apache-2.0)。
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
<p align="center">
|
|
267
|
+
Fetcher 生态系统的一部分
|
|
268
|
+
</p>
|