Skip to content

Commit e4bff0f

Browse files
committed
feat: update request
1 parent 9a3973b commit e4bff0f

File tree

5 files changed

+883
-680
lines changed

5 files changed

+883
-680
lines changed

docs/REQUESTS.md

Lines changed: 240 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,148 +1,295 @@
1-
**请求模块 使用说明**
1+
# Request 请求模块使用指南
22

33
位置: `src/service/request.js`
44

5-
简介
6-
- 本文档说明如何使用仓库中增强过的 `request` 模块。该模块基于 `axios`,提供:可取消请求、并发/串行执行、重试机制、上传/下载进度回调、每次请求可开关错误弹窗及全局配置接口。
5+
基于 Axios 封装的企业级 HTTP 请求库,提供了统一的错误处理、请求拦截、响应拦截、并发控制、文件处理等高级功能。
76

8-
导入
7+
## 引入方式
98

10-
```js
9+
```javascript
1110
import request from '@/service/request'
1211
```
1312

14-
1) 全局配置
13+
---
1514

16-
- 设置 baseURL:
15+
## 一、基础使用
1716

18-
```js
19-
request.setBaseURL('https://api.example.com')
17+
### 1. GET 请求
18+
用于获取数据,参数会自动序列化到 URL 中。
19+
20+
```javascript
21+
// 简单请求
22+
const res = await request.get('/api/users')
23+
24+
// 带参数请求: /api/users?page=1&pageSize=10
25+
const data = await request.get('/api/users', {
26+
page: 1,
27+
pageSize: 10
28+
})
2029
```
2130

22-
- 设置全局默认 headers:
31+
### 2. POST 请求
32+
用于提交数据,默认使用 `application/json`
2333

24-
```js
25-
request.setDefaultHeaders({ 'X-Trace-Id': 'abc123' })
34+
```javascript
35+
const res = await request.post('/api/users', {
36+
username: 'john_doe',
37+
38+
})
2639
```
2740

28-
2) 基本请求(返回 Promise,同时附带 `.cancel()`
41+
### 3. PUT / DELETE 请求
2942

30-
```js
31-
// GET
32-
const p = request.get('/api/users', { page: 1 })
33-
p.then(res => console.log(res)).catch(err => console.error(err))
43+
```javascript
44+
// PUT: 更新资源
45+
await request.put('/api/users/123', {
46+
role: 'admin'
47+
})
3448

35-
// 取消请求
36-
p.cancel()
49+
// DELETE: 删除资源
50+
await request.delete('/api/users/123')
51+
```
3752

38-
// POST
39-
request.post('/api/user', { name: 'alice' })
40-
.then(res => console.log(res))
41-
.catch(err => console.error(err))
53+
### 4. 表单提交
54+
自动将 Content-Type 设置为 `application/x-www-form-urlencoded` 并序列化数据。
4255

43-
// 禁用该次请求的错误弹窗
44-
request.get('/api/maybe-missing', {}, { showError: false })
56+
```javascript
57+
// 发送 application/x-www-form-urlencoded 数据
58+
await request.form('/api/login', {
59+
username: 'admin',
60+
password: 'password123'
61+
})
4562
```
4663

47-
注意:当后端采用 `{ code, data, message }` 约定时,模块会在 `code === 0` 视作成功并返回后端对象;否则会按 `message` 展示错误(可用 `showError:false` 关闭)。如果后端返回不是该结构,则直接返回原始数据。
64+
### 5. 文件上传 (带进度)
65+
支持 `FormData` 或普通对象(自动转换),并提供上传进度回调。
66+
67+
```javascript
68+
const formData = new FormData()
69+
formData.append('file', fileObject)
4870

49-
3) 上传与下载(支持进度回调)
71+
await request.upload('/api/upload', formData, {
72+
// 监听上传进度
73+
onProgress: ({ percent, loaded, total }) => {
74+
console.log(`上传进度: ${percent}%`)
75+
}
76+
})
77+
```
5078

51-
```js
52-
// 上传
53-
const fd = new FormData()
54-
fd.append('file', fileInput.files[0])
55-
request.upload('/api/upload', fd, {
56-
onUploadProgress: (e) => {
57-
const pct = Math.round((e.loaded / e.total) * 100)
58-
console.log('上传进度', pct)
59-
},
60-
}).then(res => console.log('上传完成', res))
79+
### 6. 文件下载
80+
自动处理 Blob 响应,并触发浏览器下载行为。
6181

62-
// 下载(会触发浏览器下载)
63-
request.download('/api/export', { q: 'all' }, 'report.xlsx', {
64-
onDownloadProgress: (e) => {
65-
const pct = Math.round((e.loaded / e.total) * 100)
66-
console.log('下载进度', pct)
82+
```javascript
83+
// 简单下载,文件名优先从响应头 Content-Disposition 获取
84+
await request.download('/api/export/users')
85+
```
86+
87+
### 7. 自定义文件名下载
88+
如果后端未返回文件名,或需要覆盖文件名。
89+
90+
```javascript
91+
await request.download(
92+
'/api/export/report',
93+
{ year: 2024 }, // 查询参数
94+
'2024年度报表.xlsx' // 指定文件名
95+
)
96+
```
97+
98+
### 8. 自定义 Authorization
99+
默认会自动携带 Token,如需覆盖或使用特殊 Token。
100+
101+
```javascript
102+
await request.get('/api/external/data', {}, {
103+
headers: {
104+
'Authorization': 'Bearer SPECIAL_TOKEN_123'
67105
}
68-
}).then(() => console.log('下载结束'))
106+
})
107+
```
108+
109+
### 9. 禁用重复请求取消
110+
默认情况下,相同的未完成请求会被自动取消(防抖)。如果业务需要允许重复请求(如聊天发送),可禁用此功能。
111+
112+
```javascript
113+
await request.post('/api/chat/send', { msg: 'hello' }, {
114+
cancelDuplicate: false // 允许重复发送
115+
})
116+
```
117+
118+
### 10. 自定义配置
119+
控制 Token 携带和错误提示行为。
120+
121+
```javascript
122+
await request.get('/api/public/config', {}, {
123+
needToken: false, // 不携带 Token (适用于公开接口)
124+
showError: false // 关闭默认的错误提示 (适用于需要手动处理错误的场景)
125+
})
69126
```
70127

71-
4) 并行 / 并发控制
128+
---
129+
130+
## 二、高级使用
131+
132+
### 1. 并发请求 (Parallel)
133+
同时发起多个请求,并限制最大并发数(默认 5)。
134+
135+
```javascript
136+
const userIds = [1, 2, 3, 4, 5, 6]
72137

73-
```js
74-
// 简单并行:传入请求描述数组,返回 Promise 数组结果
75-
// 描述可以是:函数、axios config 对象,或 [method, url, body, config]
76-
const results = await request.parallel([
77-
['get', '/api/a'],
78-
['post', '/api/b', { x: 1 }],
79-
() => request.get('/api/c')
80-
], /* concurrency */ 3)
138+
// 构建请求任务数组
139+
const tasks = userIds.map(id => ({
140+
method: 'GET',
141+
url: `/api/users/${id}`
142+
}))
81143

144+
// 执行并发请求,限制同时最多执行 3 个
145+
const results = await request.parallel(tasks, 3)
82146
console.log(results)
83147
```
84148

85-
5) 串行
149+
### 2. 串行请求 (Series)
150+
按顺序执行请求,前一个完成后才执行下一个(适用于有依赖关系的请求)。
86151

87-
```js
88-
// 依次执行
89-
const seriesResults = await request.series([
90-
() => request.get('/api/step1'),
91-
['post', '/api/step2', { id: 123 }],
152+
```javascript
153+
const results = await request.series([
154+
// 任务 1
155+
{ method: 'POST', url: '/api/step1', data: { init: true } },
156+
// 任务 2
157+
{ method: 'POST', url: '/api/step2', data: { confirm: true } }
92158
])
93159
```
94160

95-
6) 可取消的自定义请求
161+
或者使用函数形式处理依赖:
96162

97-
```js
98-
const promise = request.request({ method: 'get', url: '/api/long', params: { t: 1 } })
99-
// 取消
100-
promise.cancel()
163+
```javascript
164+
await request.series([
165+
async () => {
166+
const user = await request.get('/api/user/current')
167+
// 依赖上一步的结果
168+
return request.get(`/api/orders/${user.id}`)
169+
}
170+
])
101171
```
102172

103-
7) 重试(对任意返回 Promise 的函数)
104-
105-
```js
106-
await request.retry(() => request.get('/api/maybe-flaky'), 3, 1000)
173+
### 3. 轮询请求
174+
结合 `RequestUtils.delay` 实现轮询。
175+
176+
```javascript
177+
import request, { RequestUtils } from '@/service/request'
178+
179+
async function pollStatus(taskId) {
180+
while (true) {
181+
const res = await request.get(`/api/tasks/${taskId}`)
182+
183+
if (res.status === 'COMPLETED') {
184+
return res.result
185+
}
186+
187+
if (res.status === 'FAILED') {
188+
throw new Error('Task failed')
189+
}
190+
191+
// 等待 2 秒后再次请求
192+
await RequestUtils.delay(2000)
193+
}
194+
}
107195
```
108196

109-
8) 访问底层 axios(高级用法)
197+
### 4. 重试机制
198+
对于不稳定的接口,可以使用自动重试功能。
110199

111-
```js
112-
const ax = request.axios()
113-
ax.get('/raw/endpoint').then(r => console.log(r))
200+
```javascript
201+
// 如果请求失败,会自动重试 3 次,每次间隔 1000ms
202+
const data = await request.retry(
203+
() => request.get('/api/unstable-service'),
204+
3, // 重试次数
205+
1000 // 延迟时间 (ms)
206+
)
114207
```
115208

116-
9) 常见使用场景示例
117-
- 场景 A:页面 mount 时保护性加载权限数据,避免重复请求
209+
### 5. 请求队列
210+
利用 `parallel` 的并发限制特性实现请求队列。
211+
212+
```javascript
213+
// 假设有 100 个文件需要处理
214+
const files = [...]
118215

119-
```js
120-
// 使用 permissionService(单例)来避免多个组件重复向后端请求权限
121-
import permissionService from '@/service/permissionService'
122-
// 在 App 初始化处调用一次
123-
permissionService.getPermissions()
216+
const uploadTasks = files.map(file => () => {
217+
return request.upload('/api/upload', { file })
218+
})
219+
220+
// 创建一个最大并发数为 2 的上传队列
221+
// 只有当前面的请求完成,后续请求才会开始
222+
await request.parallel(uploadTasks, 2)
124223
```
125224

126-
- 场景 B:文件导出,提供进度与取消
225+
### 6. 批量上传文件(限制并发数)
226+
同上,这是处理大批量文件上传的最佳实践。
227+
228+
```javascript
229+
const fileList = [file1, file2, file3, ...]
127230

128-
```js
129-
const downloadPromise = request.download('/api/export', {}, 'report.xlsx', {
130-
onDownloadProgress: (e) => console.log('progress', e.loaded)
231+
// 封装上传任务
232+
const tasks = fileList.map(file => {
233+
return () => request.upload('/api/files', { file }, {
234+
onProgress: (p) => console.log(`${file.name}: ${p.percent}%`)
235+
})
131236
})
132-
// 在需要时取消
133-
// downloadPromise.cancel()
237+
238+
// 限制同时上传 3 个文件
239+
await request.parallel(tasks, 3)
134240
```
135241

136-
- 场景 C:批量请求但限制并发(防止后端压力)
242+
### 7. 全局请求前置处理
243+
`request.js` 内部已内置了请求追踪和性能统计功能。
137244

138-
```js
139-
const tasks = urls.map((u) => ['get', u])
140-
const results = await request.parallel(tasks, 4)
245+
**请求追踪 (Request ID):**
246+
可以通过传入 `requestId` 来标记特定请求,方便在日志中追踪。
247+
248+
```javascript
249+
import { v4 as uuidv4 } from 'uuid'
250+
251+
request.get('/api/trace-test', {}, {
252+
requestId: uuidv4() // 会添加到 Headers: X-Request-ID
253+
})
141254
```
142255

143-
备注
144-
- 默认超时时间与 baseURL 可通过 `request.setBaseURL``request.setDefaultHeaders` 调整。
145-
- 单次请求关闭弹窗:传 `config.showError = false`
146-
- 所有请求返回的 Promise 都包含 `.cancel()`(内部使用 AbortController)。
256+
**响应时间统计:**
257+
开发模式下,控制台会自动输出每个请求的耗时。
258+
```
259+
[Logger] 请求完成: GET /api/users [150ms]
260+
```
261+
262+
如果需要在业务代码中获取耗时,可以在拦截器中扩展(需修改 `request.js`),或者简单地在调用处计算:
263+
264+
```javascript
265+
const start = Date.now()
266+
await request.get('/api/data')
267+
const duration = Date.now() - start
268+
console.log(`请求耗时: ${duration}ms`)
269+
```
147270

148-
如需我把示例中的某些场景自动替换到代码中(例如把多个直接调用 `permissionAPI.getUserPermissions()` 的位置替换为 `permissionService.getPermissions()`),我可以继续扫描并提交补丁。
271+
---
272+
273+
## 三、API 参考
274+
275+
### 配置对象 (Config)
276+
| 属性 | 类型 | 默认值 | 说明 |
277+
|---|---|---|---|
278+
| `cancelDuplicate` | boolean | `true` | 是否自动取消重复的进行中请求 |
279+
| `needToken` | boolean | `true` | 是否自动在 Header 中携带 Token |
280+
| `showError` | boolean | `true` | 请求失败时是否自动弹出错误提示 |
281+
| `requestId` | string | - | 自定义请求 ID,将放入 `X-Request-ID`|
282+
| `returnFullResponse` | boolean | `false` | 是否返回完整的 Axios Response 对象,默认只返回 `data` |
283+
| `onProgress` | function | - | 上传/下载进度回调 |
284+
285+
### 核心方法
286+
- `request.get(url, params, config)`
287+
- `request.post(url, data, config)`
288+
- `request.put(url, data, config)`
289+
- `request.delete(url, params, config)`
290+
- `request.form(url, data, config)`
291+
- `request.upload(url, data, config)`
292+
- `request.download(url, params, fileName, config)`
293+
- `request.parallel(tasks, concurrency)`
294+
- `request.series(tasks)`
295+
- `request.retry(fn, attempts, delay)`

0 commit comments

Comments
 (0)