uniapp-request-sdk 1.7.0 → 1.7.1

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 CHANGED
@@ -109,27 +109,15 @@ export function updateUserProfile(data: Record<string, any>) {
109
109
  }
110
110
 
111
111
  export function uploadAvatar(filePath: string) {
112
- return request.uploadFile(
113
- '/user/avatar/upload',
114
- filePath,
115
- undefined,
116
- 'file',
117
- undefined,
118
- (progress) => {
119
- console.log('上传进度:', progress.progress);
120
- },
121
- );
112
+ return request.uploadFile('/user/avatar/upload', filePath, undefined, 'file', undefined, (progress) => {
113
+ console.log('上传进度:', progress.progress);
114
+ });
122
115
  }
123
116
 
124
117
  export function downloadDocument(documentId: string) {
125
- return request.downloadFile(
126
- `/documents/${documentId}/download`,
127
- undefined,
128
- undefined,
129
- (progress) => {
130
- console.log('下载进度:', progress.progress);
131
- },
132
- );
118
+ return request.downloadFile(`/documents/${documentId}/download`, undefined, undefined, (progress) => {
119
+ console.log('下载进度:', progress.progress);
120
+ });
133
121
  }
134
122
  ```
135
123
 
@@ -180,14 +168,9 @@ const uploadResult = await request.uploadFile(
180
168
  );
181
169
 
182
170
  // 7. 下载文件
183
- const downloadPath = await request.downloadFile(
184
- '/download/document.pdf',
185
- undefined,
186
- undefined,
187
- (progress) => {
188
- console.log('当前下载进度:', progress.progress);
189
- },
190
- );
171
+ const downloadPath = await request.downloadFile('/download/document.pdf', undefined, undefined, (progress) => {
172
+ console.log('当前下载进度:', progress.progress);
173
+ });
191
174
  ```
192
175
 
193
176
  ## 工程接入建议
@@ -224,40 +207,44 @@ src/
224
207
  ```typescript
225
208
  const request = new UniRequest({
226
209
  // 基础配置
227
- baseUrl: 'https://api.example.com', // API 基础地址(必须)
228
- username: 'john_doe', // 用户名(用于日志)
210
+ baseUrl: 'https://api.example.com', // API 基础地址(必须)
211
+ username: 'john_doe', // 用户名(用于日志)
229
212
 
230
213
  // 超时设置
231
- timeout: 10000, // 普通请求超时(毫秒,默认 10s)
232
- uploadTimeout: 5000, // 文件上传超时(毫秒,默认 5s)
214
+ timeout: 10000, // 普通请求超时(毫秒,默认 10s)
215
+ uploadTimeout: 5000, // 文件上传超时(毫秒,默认 5s)
233
216
 
234
217
  // 重试配置
235
- maxRetryCount: 3, // 最大重试次数(默认 3)
236
- retryDelay: 3000, // 重试延迟时间(毫秒,默认 3s)
218
+ maxRetryCount: 3, // 最大重试次数(默认 3)
219
+ retryDelay: 3000, // 重试延迟时间(毫秒,默认 3s)
237
220
 
238
221
  // 请求头配置
239
- header: { // 全局请求头
222
+ header: {
223
+ // 全局请求头
240
224
  'Content-Type': 'application/json',
241
225
  'X-Custom-Header': 'value',
242
226
  },
243
227
 
244
228
  // Token 配置
245
- token: 'initial-token', // 初始 token
246
- tokenHeader: 'Authorization', // token 所在的 header 字段名(默认)
247
- tokenPrefix: 'Bearer ', // token 前缀(默认带空格)
248
- tokenEventName: 'getToken', // 向 APP 获取 token 的事件名
249
- getTokenFun: async () => { // 自定义获取 token 函数
229
+ token: 'initial-token', // 初始 token
230
+ tokenHeader: 'Authorization', // token 所在的 header 字段名(默认)
231
+ tokenPrefix: 'Bearer ', // token 前缀(默认带空格)
232
+ tokenEventName: 'getToken', // 向 APP 获取 token 的事件名
233
+ getTokenFun: async () => {
234
+ // 自定义获取 token 函数
250
235
  return await fetchTokenFromStorage();
251
236
  },
252
237
 
253
238
  // 错误处理
254
- onErrorHandler: (error) => { // 统一错误处理函数
239
+ onErrorHandler: (error) => {
240
+ // 统一错误处理函数
255
241
  console.error('请求错误:', error);
256
242
  // 可以在这里上报错误日志
257
243
  },
258
244
 
259
245
  // 新增:请求头预处理
260
- headerProcessor: async (header) => { // 动态处理请求头(异步)
246
+ headerProcessor: async (header) => {
247
+ // 动态处理请求头(异步)
261
248
  const timestamp = Date.now();
262
249
  const signature = await generateSignature(timestamp);
263
250
  return {
@@ -273,9 +260,10 @@ const request = new UniRequest({
273
260
  ```typescript
274
261
  // 使用 setParams 方法动态更新配置
275
262
  request.setParams({
276
- token: 'new-token', // 更新 token
277
- baseUrl: 'https://new-api.example.com', // 更新 API 地址
278
- headerProcessor: (header) => { // 更新头处理器
263
+ token: 'new-token', // 更新 token
264
+ baseUrl: 'https://new-api.example.com', // 更新 API 地址
265
+ headerProcessor: (header) => {
266
+ // 更新头处理器
279
267
  return {
280
268
  'X-New-Header': 'new-value',
281
269
  };
@@ -303,7 +291,7 @@ const data = await request.get<ResponseType>(
303
291
  {},
304
292
  {
305
293
  'X-Custom-Header': 'custom-value',
306
- }
294
+ },
307
295
  );
308
296
  ```
309
297
 
@@ -322,7 +310,7 @@ const result = await request.post<ResponseType>(
322
310
  { name: 'John' },
323
311
  {
324
312
  'X-Custom-Header': 'custom-value',
325
- }
313
+ },
326
314
  );
327
315
  ```
328
316
 
@@ -345,29 +333,27 @@ const result = await request.put<ResponseType>('/users/1', {
345
333
  ```typescript
346
334
  // 基础文件上传
347
335
  const result = await request.uploadFile<ResponseType>(
348
- '/upload', // 上传 URL
349
- '/path/to/file.jpg' // 文件路径
336
+ '/upload', // 上传 URL
337
+ '/path/to/file.jpg', // 文件路径
350
338
  );
351
339
 
352
340
  // 带 FormData 的文件上传
353
- const result = await request.uploadFile<ResponseType>(
354
- '/upload',
355
- '/path/to/file.jpg',
356
- { // FormData 字段
357
- description: 'My upload',
358
- category: 'profile',
359
- }
360
- );
341
+ const result = await request.uploadFile<ResponseType>('/upload', '/path/to/file.jpg', {
342
+ // FormData 字段
343
+ description: 'My upload',
344
+ category: 'profile',
345
+ });
361
346
 
362
347
  // 自定义文件字段名和请求头
363
348
  const result = await request.uploadFile<ResponseType>(
364
349
  '/upload',
365
350
  '/path/to/file.jpg',
366
351
  { description: 'My upload' },
367
- 'avatar', // 文件字段名(默认 'file')
368
- { // 自定义请求头
352
+ 'avatar', // 文件字段名(默认 'file')
353
+ {
354
+ // 自定义请求头
369
355
  'X-Upload-Token': 'upload-token',
370
- }
356
+ },
371
357
  );
372
358
 
373
359
  // 监听上传进度
@@ -381,7 +367,7 @@ const result = await request.uploadFile<ResponseType>(
381
367
  console.log('上传进度百分比:', progress.progress);
382
368
  console.log('已上传字节数:', progress.totalBytesSent);
383
369
  console.log('总字节数:', progress.totalBytesExpectedToSend);
384
- }
370
+ },
385
371
  );
386
372
  ```
387
373
 
@@ -394,29 +380,25 @@ const tempFilePath = await request.downloadFile('/download/file.pdf');
394
380
  // 带自定义保存路径的下载(仅小程序有效)
395
381
  const filePath = await request.downloadFile(
396
382
  '/download/file.pdf',
397
- 'user_documents/file.pdf' // 本地保存路径(仅小程序平台支持)
383
+ 'user_documents/file.pdf', // 本地保存路径(仅小程序平台支持)
398
384
  );
399
385
 
400
386
  // 自定义请求头的下载
401
387
  const filePath = await request.downloadFile(
402
388
  '/download/file.pdf',
403
- undefined, // 保存路径
404
- { // 自定义请求头
389
+ undefined, // 保存路径
390
+ {
391
+ // 自定义请求头
405
392
  'X-Download-Token': 'download-token',
406
- }
393
+ },
407
394
  );
408
395
 
409
396
  // 监听下载进度
410
- const filePath = await request.downloadFile(
411
- '/download/large-file.zip',
412
- undefined,
413
- undefined,
414
- (progress) => {
415
- console.log('下载进度百分比:', progress.progress);
416
- console.log('已下载字节数:', progress.totalBytesWritten);
417
- console.log('总字节数:', progress.totalBytesExpectedToWrite);
418
- }
419
- );
397
+ const filePath = await request.downloadFile('/download/large-file.zip', undefined, undefined, (progress) => {
398
+ console.log('下载进度百分比:', progress.progress);
399
+ console.log('已下载字节数:', progress.totalBytesWritten);
400
+ console.log('总字节数:', progress.totalBytesExpectedToWrite);
401
+ });
420
402
  ```
421
403
 
422
404
  ## 文件上传
@@ -436,14 +418,14 @@ uploadFile<T>(
436
418
 
437
419
  ### 参数说明
438
420
 
439
- | 参数 | 类型 | 是否必填 | 说明 |
440
- |------|------|----------|------|
441
- | `url` | `string` | 是 | 上传接口地址,支持相对路径和完整 URL |
442
- | `filePath` | `string` | 是 | 待上传文件的本地临时路径 |
443
- | `formData` | `Record<string, any>` | 否 | 附加的表单字段 |
444
- | `name` | `string` | 否 | 文件字段名,默认是 `file` |
445
- | `header` | `Record<string, string>` | 否 | 当前上传请求的自定义请求头 |
446
- | `onProgressUpdateCallback` | `(result) => void` | 否 | 上传进度回调 |
421
+ | 参数 | 类型 | 是否必填 | 说明 |
422
+ | -------------------------- | ------------------------ | -------- | ------------------------------------ |
423
+ | `url` | `string` | 是 | 上传接口地址,支持相对路径和完整 URL |
424
+ | `filePath` | `string` | 是 | 待上传文件的本地临时路径 |
425
+ | `formData` | `Record<string, any>` | 否 | 附加的表单字段 |
426
+ | `name` | `string` | 否 | 文件字段名,默认是 `file` |
427
+ | `header` | `Record<string, string>` | 否 | 当前上传请求的自定义请求头 |
428
+ | `onProgressUpdateCallback` | `(result) => void` | 否 | 上传进度回调 |
447
429
 
448
430
  ### 上传进度回调说明
449
431
 
@@ -492,12 +474,12 @@ downloadFile<T extends string = string>(
492
474
 
493
475
  ### 参数说明
494
476
 
495
- | 参数 | 类型 | 是否必填 | 说明 |
496
- |------|------|----------|------|
497
- | `url` | `string` | 是 | 下载资源的 URL,支持相对路径和完整 URL |
498
- | `filePath` | `string` | 否 | 文件保存路径(本地路径),仅小程序平台支持;如不指定则保存到临时目录 |
499
- | `header` | `Record<string, string>` | 否 | 当前下载请求的自定义请求头 |
500
- | `onProgressUpdateCallback` | `(result) => void` | 否 | 下载进度回调 |
477
+ | 参数 | 类型 | 是否必填 | 说明 |
478
+ | -------------------------- | ------------------------ | -------- | -------------------------------------------------------------------- |
479
+ | `url` | `string` | 是 | 下载资源的 URL,支持相对路径和完整 URL |
480
+ | `filePath` | `string` | 否 | 文件保存路径(本地路径),仅小程序平台支持;如不指定则保存到临时目录 |
481
+ | `header` | `Record<string, string>` | 否 | 当前下载请求的自定义请求头 |
482
+ | `onProgressUpdateCallback` | `(result) => void` | 否 | 下载进度回调 |
501
483
 
502
484
  ### 返回值说明
503
485
 
@@ -520,30 +502,30 @@ const filePath: string = await request.downloadFile<string>('/files/document.pdf
520
502
 
521
503
  ```typescript
522
504
  type DownloadFileProgressUpdateResult = {
523
- progress?: number; // 下载进度百分比 (0-100)
524
- totalBytesWritten?: number; // 已下载字节数
525
- totalBytesExpectedToWrite?: number; // 总字节数
505
+ progress?: number; // 下载进度百分比 (0-100)
506
+ totalBytesWritten?: number; // 已下载字节数
507
+ totalBytesExpectedToWrite?: number; // 总字节数
526
508
  };
527
509
  ```
528
510
 
529
511
  ### 与上传的区别
530
512
 
531
- | 特性 | uploadFile | downloadFile |
532
- |------|-----------|--------------|
533
- | 超时默认值 | 5 秒 | 30 秒 |
534
- | 文件来源 | 本地文件 | 远程 URL |
535
- | 附加参数 | formData(表单字段) | filePath(保存路径) |
536
- | 进度类型 | `totalBytesSent` | `totalBytesWritten` |
537
- | 返回值 | 服务端响应的 data 字段 | 文件临时路径 |
513
+ | 特性 | uploadFile | downloadFile |
514
+ | ---------- | ---------------------- | -------------------- |
515
+ | 超时默认值 | 5 秒 | 30 秒 |
516
+ | 文件来源 | 本地文件 | 远程 URL |
517
+ | 附加参数 | formData(表单字段) | filePath(保存路径) |
518
+ | 进度类型 | `totalBytesSent` | `totalBytesWritten` |
519
+ | 返回值 | 服务端响应的 data 字段 | 文件临时路径 |
538
520
 
539
521
  ### 平台兼容性
540
522
 
541
- | 特性 | iOS App | Android App | 小程序 | H5 |
542
- |------|---------|-----------|--------|-----|
543
- | 基础下载 | ✅ | ✅ | ✅ | ✅ |
544
- | 进度监听 | ✅ | ✅ | ✅ | 部分 |
545
- | 自定义保存路径 | ❌ | ❌ | ✅ | ❌ |
546
- | 文件 URI | ✅ | ✅ | ✅ | 部分 |
523
+ | 特性 | iOS App | Android App | 小程序 | H5 |
524
+ | -------------- | ------- | ----------- | ------ | ---- |
525
+ | 基础下载 | ✅ | ✅ | ✅ | ✅ |
526
+ | 进度监听 | ✅ | ✅ | ✅ | 部分 |
527
+ | 自定义保存路径 | ❌ | ❌ | ✅ | ❌ |
528
+ | 文件 URI | ✅ | ✅ | ✅ | 部分 |
547
529
 
548
530
  ### 使用注意事项
549
531
 
@@ -585,7 +567,7 @@ import UniRequest from 'uniapp-request-sdk';
585
567
 
586
568
  const request = new UniRequest({
587
569
  baseUrl: 'https://api.example.com',
588
- tokenEventName: 'getToken', // APP 端事件名
570
+ tokenEventName: 'getToken', // APP 端事件名
589
571
  onErrorHandler: (error) => {
590
572
  if (error.statusCode === 401) {
591
573
  console.log('用户已登出');
@@ -638,7 +620,7 @@ import UniRequest from 'uniapp-request-sdk';
638
620
 
639
621
  const request = new UniRequest({
640
622
  baseUrl: 'https://api.example.com',
641
- uploadTimeout: 30000, // 文件上传超时 30 秒
623
+ uploadTimeout: 30000, // 文件上传超时 30 秒
642
624
  });
643
625
 
644
626
  // 选择文件并上传
@@ -647,15 +629,11 @@ async function selectAndUploadFile() {
647
629
  count: 1,
648
630
  success: async (res) => {
649
631
  try {
650
- const result = await request.uploadFile(
651
- '/upload',
652
- res.tempFilePaths[0],
653
- {
654
- // 附加信息
655
- description: '头像',
656
- category: 'avatar',
657
- }
658
- );
632
+ const result = await request.uploadFile('/upload', res.tempFilePaths[0], {
633
+ // 附加信息
634
+ description: '头像',
635
+ category: 'avatar',
636
+ });
659
637
  console.log('上传成功:', result);
660
638
  } catch (error) {
661
639
  console.error('上传失败:', error);
@@ -713,7 +691,7 @@ import UniRequest from 'uniapp-request-sdk';
713
691
 
714
692
  const request = new UniRequest({
715
693
  baseUrl: 'https://api.example.com',
716
- downloadTimeout: 30000, // 文件下载超时 30 秒
694
+ downloadTimeout: 30000, // 文件下载超时 30 秒
717
695
  });
718
696
 
719
697
  // 简单下载
@@ -737,19 +715,14 @@ async function downloadFileWithProgress() {
737
715
  try {
738
716
  let downloadProgress = 0;
739
717
 
740
- const filePath = await request.downloadFile(
741
- '/files/large-file.zip',
742
- undefined,
743
- undefined,
744
- (progress) => {
745
- downloadProgress = progress.progress || 0;
746
- console.log(`下载进度: ${downloadProgress}%`);
747
- console.log(`已下载: ${progress.totalBytesWritten} / ${progress.totalBytesExpectedToWrite} 字节`);
718
+ const filePath = await request.downloadFile('/files/large-file.zip', undefined, undefined, (progress) => {
719
+ downloadProgress = progress.progress || 0;
720
+ console.log(`下载进度: ${downloadProgress}%`);
721
+ console.log(`已下载: ${progress.totalBytesWritten} / ${progress.totalBytesExpectedToWrite} 字节`);
748
722
 
749
- // 更新 UI 进度条
750
- // updateProgressBar(downloadProgress);
751
- }
752
- );
723
+ // 更新 UI 进度条
724
+ // updateProgressBar(downloadProgress);
725
+ });
753
726
 
754
727
  console.log('下载完成:', filePath);
755
728
  } catch (error) {
@@ -762,7 +735,7 @@ async function downloadFileToPath() {
762
735
  try {
763
736
  const filePath = await request.downloadFile(
764
737
  '/files/contract.pdf',
765
- 'user_documents/contract.pdf' // 保存到应用沙箱
738
+ 'user_documents/contract.pdf', // 保存到应用沙箱
766
739
  );
767
740
 
768
741
  console.log('文件已保存:', filePath);
@@ -783,7 +756,7 @@ async function downloadSecureFile() {
783
756
  },
784
757
  (progress) => {
785
758
  console.log(`下载进度: ${progress.progress}%`);
786
- }
759
+ },
787
760
  );
788
761
 
789
762
  console.log('下载完成:', filePath);
@@ -817,7 +790,7 @@ export const FileService = {
817
790
  undefined,
818
791
  (progress) => {
819
792
  console.log(`上传进度: ${progress.progress}%`);
820
- }
793
+ },
821
794
  );
822
795
  return result;
823
796
  } catch (error) {
@@ -835,7 +808,7 @@ export const FileService = {
835
808
  { 'X-Document-Id': documentId },
836
809
  (progress) => {
837
810
  console.log(`下载进度: ${progress.progress}%`);
838
- }
811
+ },
839
812
  );
840
813
  return filePath;
841
814
  } catch (error) {
@@ -874,13 +847,10 @@ export default {
874
847
  count: 1,
875
848
  });
876
849
 
877
- const result = await FileService.uploadDocument(
878
- res.tempFilePaths[0],
879
- {
880
- description: '重要文档',
881
- category: 'reports',
882
- }
883
- );
850
+ const result = await FileService.uploadDocument(res.tempFilePaths[0], {
851
+ description: '重要文档',
852
+ category: 'reports',
853
+ });
884
854
 
885
855
  uni.showToast({
886
856
  title: '上传成功',
@@ -973,6 +943,7 @@ const request = new UniRequest({
973
943
  ```
974
944
 
975
945
  其中:
946
+
976
947
  - `errno === 0` 表示请求成功
977
948
  - `errno !== 0` 表示业务错误,会触发 reject
978
949
 
@@ -980,13 +951,13 @@ const request = new UniRequest({
980
951
 
981
952
  ### 自动处理的错误
982
953
 
983
- | 错误类型 | 处理方式 |
984
- |---------|---------|
985
- | HTTP 403 | 自动获取新 token 并重试 |
954
+ | 错误类型 | 处理方式 |
955
+ | -------- | ------------------------------------- |
956
+ | HTTP 403 | 自动获取新 token 并重试 |
986
957
  | HTTP 401 | 触发 logout 事件,通知 APP 返回登录页 |
987
- | 网络错误 | 自动重试(最多 maxRetryCount 次) |
988
- | 超时错误 | 自动重试(最多 maxRetryCount 次) |
989
- | 业务错误 | 直接 reject,业务处理 |
958
+ | 网络错误 | 自动重试(最多 maxRetryCount 次) |
959
+ | 超时错误 | 自动重试(最多 maxRetryCount 次) |
960
+ | 业务错误 | 直接 reject,业务处理 |
990
961
 
991
962
  ### 全局错误处理
992
963
 
@@ -1044,28 +1015,28 @@ const request = new UniRequest({
1044
1015
 
1045
1016
  ```typescript
1046
1017
  const request = new UniRequest({
1047
- tokenEventName: 'getToken', // APP 端对应事件名
1018
+ tokenEventName: 'getToken', // APP 端对应事件名
1048
1019
  });
1049
1020
  ```
1050
1021
 
1051
1022
  ## 配置参数详解
1052
1023
 
1053
- | 参数 | 类型 | 默认值 | 必填 | 说明 |
1054
- |-----|------|-------|------|------|
1055
- | baseUrl | string | - | ✅ | API 基础地址,支持代理路径 |
1056
- | timeout | number | 10000 | - | 普通请求超时(毫秒) |
1057
- | uploadTimeout | number | 5000 | - | 文件上传超时(毫秒) |
1058
- | maxRetryCount | number | 3 | - | 失败重试次数 |
1059
- | retryDelay | number | 3000 | - | 重试延迟时间(毫秒) |
1060
- | header | object | - | - | 全局请求头 |
1061
- | headerProcessor | function | - | - | 请求头预处理函数(新增) |
1062
- | token | string | - | - | 初始 token |
1063
- | tokenHeader | string | Authorization | - | token 所在 header 字段 |
1064
- | tokenPrefix | string | 'Bearer ' | - | token 前缀 |
1065
- | tokenEventName | string | getToken | - | APP 获取 token 的事件名 |
1066
- | getTokenFun | function | - | - | 自定义 token 获取函数 |
1067
- | username | string | - | - | 用户名(用于日志) |
1068
- | onErrorHandler | function | console.error | - | 全局错误处理函数 |
1024
+ | 参数 | 类型 | 默认值 | 必填 | 说明 |
1025
+ | --------------- | -------- | ------------- | ---- | -------------------------- |
1026
+ | baseUrl | string | - | ✅ | API 基础地址,支持代理路径 |
1027
+ | timeout | number | 10000 | - | 普通请求超时(毫秒) |
1028
+ | uploadTimeout | number | 5000 | - | 文件上传超时(毫秒) |
1029
+ | maxRetryCount | number | 3 | - | 失败重试次数 |
1030
+ | retryDelay | number | 3000 | - | 重试延迟时间(毫秒) |
1031
+ | header | object | - | - | 全局请求头 |
1032
+ | headerProcessor | function | - | - | 请求头预处理函数(新增) |
1033
+ | token | string | - | - | 初始 token |
1034
+ | tokenHeader | string | Authorization | - | token 所在 header 字段 |
1035
+ | tokenPrefix | string | 'Bearer ' | - | token 前缀 |
1036
+ | tokenEventName | string | getToken | - | APP 获取 token 的事件名 |
1037
+ | getTokenFun | function | - | - | 自定义 token 获取函数 |
1038
+ | username | string | - | - | 用户名(用于日志) |
1039
+ | onErrorHandler | function | console.error | - | 全局错误处理函数 |
1069
1040
 
1070
1041
  ## 请求头预处理器(新增功能)
1071
1042
 
@@ -1134,7 +1105,7 @@ const request = new UniRequest({
1134
1105
  ### 预处理器的执行时机
1135
1106
 
1136
1107
  - **执行点**:在设置 token 后、发送请求前
1137
- - **执行频率**:每次请求都执行(包括重试)
1108
+ - **执行频率**:每次业务请求执行一次,自动重试时复用第一次处理后的 header;若中途刷新 token,则会基于新 token 重新执行一次
1138
1109
  - **异常处理**:如果预处理器异常,请求会直接 reject,不会发送
1139
1110
 
1140
1111
  ### 预处理器的签名
@@ -1215,15 +1186,15 @@ async function getUsers() {
1215
1186
  ```typescript
1216
1187
  // ❌ 错误:每次请求都会等待 5 秒
1217
1188
  headerProcessor: async () => {
1218
- await sleep(5000); // 不必要的延迟
1189
+ await sleep(5000); // 不必要的延迟
1219
1190
  return {};
1220
- }
1191
+ };
1221
1192
 
1222
1193
  // ✅ 正确:提前准备数据
1223
1194
  headerProcessor: async () => {
1224
1195
  const cachedData = await cache.get('businessData');
1225
1196
  return { 'X-Business-Id': cachedData.id };
1226
- }
1197
+ };
1227
1198
  ```
1228
1199
 
1229
1200
  3. **不要在头预处理器中设置 cookie(H5 平台)**
@@ -1231,13 +1202,13 @@ headerProcessor: async () => {
1231
1202
  ```typescript
1232
1203
  // ❌ 错误:H5 中不生效
1233
1204
  headerProcessor: async () => {
1234
- return { 'cookie': 'sessionid=xxx' };
1235
- }
1205
+ return { cookie: 'sessionid=xxx' };
1206
+ };
1236
1207
 
1237
1208
  // ✅ 正确:使用其他方式
1238
1209
  headerProcessor: async () => {
1239
1210
  return { 'X-Session-Id': 'xxx' };
1240
- }
1211
+ };
1241
1212
  ```
1242
1213
 
1243
1214
  ## 企业级实战案例
@@ -1294,16 +1265,16 @@ function onErrorHandler(resData: any) {
1294
1265
 
1295
1266
  // 主请求实例(用于常规业务请求)
1296
1267
  export const requestInstance = new UniRequest({
1297
- timeout: 4000, // 请求超时 4 秒
1298
- maxRetryCount: 2, // 重试 2 次
1299
- uploadTimeout: 1000 * 60 * 2, // 文件上传超时 2 分钟
1300
- onErrorHandler, // 使用统一错误处理
1268
+ timeout: 4000, // 请求超时 4 秒
1269
+ maxRetryCount: 2, // 重试 2 次
1270
+ uploadTimeout: 1000 * 60 * 2, // 文件上传超时 2 分钟
1271
+ onErrorHandler, // 使用统一错误处理
1301
1272
  });
1302
1273
 
1303
1274
  // AI 请求实例(用于 AI 接口请求)
1304
1275
  export const aiRequestInstance = new UniRequest({
1305
- uploadTimeout: 1000 * 20, // 上传超时 20 秒
1306
- maxRetryCount: 0, // 不重试(AI 接口可能耗时较长)
1276
+ uploadTimeout: 1000 * 20, // 上传超时 20 秒
1277
+ maxRetryCount: 0, // 不重试(AI 接口可能耗时较长)
1307
1278
  onErrorHandler,
1308
1279
  });
1309
1280
  ```
@@ -1324,8 +1295,7 @@ export const ProcessServes = {
1324
1295
  requestInstance.post<{ data: any }>('/process/application/business/readProcessMessage', payload),
1325
1296
 
1326
1297
  // 查询申请人职位
1327
- getById: (payload: { id: string }) =>
1328
- requestInstance.post('/usercenter/oaUser/getById', payload),
1298
+ getById: (payload: { id: string }) => requestInstance.post('/usercenter/oaUser/getById', payload),
1329
1299
 
1330
1300
  // 撤回流程
1331
1301
  processRecall: (payload: { businessId: string; processCategory: string; comment: string }) =>
@@ -1336,8 +1306,7 @@ export const ProcessServes = {
1336
1306
  requestInstance.post<{ data: any[] }>('/process/application/matter/list', payload),
1337
1307
 
1338
1308
  // 获取事项信息
1339
- getMatterInfo: (payload: any) =>
1340
- requestInstance.post('/process/application/matter/matterInfo', payload),
1309
+ getMatterInfo: (payload: any) => requestInstance.post('/process/application/matter/matterInfo', payload),
1341
1310
 
1342
1311
  // 流程审批
1343
1312
  processApprove: (payload: {
@@ -1347,12 +1316,10 @@ export const ProcessServes = {
1347
1316
  comment: string;
1348
1317
  pass: boolean;
1349
1318
  attachment: Array<{ name: string; url: string }>;
1350
- }) =>
1351
- requestInstance.post('/process/application/process/approve', payload),
1319
+ }) => requestInstance.post('/process/application/process/approve', payload),
1352
1320
 
1353
1321
  // 保存并审批
1354
- processSaveAndApprove: (payload: any) =>
1355
- requestInstance.post('/process/application/process/saveAndApprove', payload),
1322
+ processSaveAndApprove: (payload: any) => requestInstance.post('/process/application/process/saveAndApprove', payload),
1356
1323
 
1357
1324
  // 保存流程
1358
1325
  processSave: (payload: {
@@ -1365,8 +1332,7 @@ export const ProcessServes = {
1365
1332
  sponsorDepartmentId: string;
1366
1333
  processCategory: string;
1367
1334
  businessId?: string;
1368
- }) =>
1369
- requestInstance.post('/process/application/business/save', payload),
1335
+ }) => requestInstance.post('/process/application/business/save', payload),
1370
1336
 
1371
1337
  // 申请流程
1372
1338
  processApply: (payload: { businessId: string; ccUser: any[]; processCategory: string }) =>
@@ -1397,12 +1363,10 @@ export const ProcessServes = {
1397
1363
  departmentId: string;
1398
1364
  subjects: string[];
1399
1365
  businessLineUuids: string[];
1400
- }) =>
1401
- requestInstance.post('/financial/budgetInventory/budgetInventoryProgress', payload),
1366
+ }) => requestInstance.post('/financial/budgetInventory/budgetInventoryProgress', payload),
1402
1367
 
1403
1368
  // 获取业务线列表
1404
- getBusinessLineList: () =>
1405
- requestInstance.post('financial/businessLine/businessLineList'),
1369
+ getBusinessLineList: () => requestInstance.post('financial/businessLine/businessLineList'),
1406
1370
 
1407
1371
  // 获取合同详情
1408
1372
  getContractDetail: (payload: { contractNumber: string; businessId: string }) =>
@@ -1544,6 +1508,7 @@ export default {
1544
1508
  项目使用两个不同的请求实例:
1545
1509
 
1546
1510
  - **requestInstance**: 用于常规业务请求
1511
+
1547
1512
  - 超时时间:4 秒
1548
1513
  - 重试次数:2 次
1549
1514
  - 适合快速响应的 API
@@ -1556,6 +1521,7 @@ export default {
1556
1521
  ##### 统一错误处理
1557
1522
 
1558
1523
  所有错误都通过 `onErrorHandler` 函数处理:
1524
+
1559
1525
  - 业务错误(errno = 2002):显示错误信息,关闭小程序
1560
1526
  - 权限错误(statusCode = 403):引导用户重新登录
1561
1527
  - 其他错误:显示通用错误信息
@@ -1563,6 +1529,7 @@ export default {
1563
1529
  ##### API 层封装
1564
1530
 
1565
1531
  将所有 API 调用封装在专门的服务文件中:
1532
+
1566
1533
  - 统一的错误处理
1567
1534
  - 类型安全的请求和响应
1568
1535
  - 易于测试和维护
@@ -1578,8 +1545,6 @@ export default {
1578
1545
  ✅ **类型安全** - 充分利用 TypeScript 的泛型机制
1579
1546
  ✅ **易于扩展** - 新 API 添加无需修改现有代码
1580
1547
 
1581
-
1582
-
1583
1548
  ## 常见问题
1584
1549
 
1585
1550
  ### Q: 如何处理跨域问题?
@@ -1588,7 +1553,7 @@ A: 在 `baseUrl` 中包含代理路径,让后端或开发服务器代理请求
1588
1553
 
1589
1554
  ```typescript
1590
1555
  const request = new UniRequest({
1591
- baseUrl: 'https://localhost:8080/api/proxy/', // 包含代理路径
1556
+ baseUrl: 'https://localhost:8080/api/proxy/', // 包含代理路径
1592
1557
  });
1593
1558
  ```
1594
1559
 
@@ -1603,9 +1568,7 @@ for (const filePath of filePaths) {
1603
1568
  }
1604
1569
 
1605
1570
  // 方式 2:并行上传(不推荐,可能导致超时)
1606
- await Promise.all(
1607
- filePaths.map(filePath => request.uploadFile('/upload', filePath))
1608
- );
1571
+ await Promise.all(filePaths.map((filePath) => request.uploadFile('/upload', filePath)));
1609
1572
  ```
1610
1573
 
1611
1574
  ### Q: 如何下载多个文件?
@@ -1621,9 +1584,7 @@ for (const fileUrl of fileUrls) {
1621
1584
  }
1622
1585
 
1623
1586
  // 方式 2:并行下载(需谨慎,可能导致超时)
1624
- const filePaths = await Promise.all(
1625
- fileUrls.map(fileUrl => request.downloadFile(fileUrl))
1626
- );
1587
+ const filePaths = await Promise.all(fileUrls.map((fileUrl) => request.downloadFile(fileUrl)));
1627
1588
  ```
1628
1589
 
1629
1590
  ### Q: 下载的文件如何持久化?
@@ -1649,12 +1610,12 @@ A: 增加 `downloadTimeout` 的值:
1649
1610
  ```typescript
1650
1611
  const request = new UniRequest({
1651
1612
  baseUrl: 'https://api.example.com',
1652
- downloadTimeout: 60000, // 60 秒,适合大文件
1613
+ downloadTimeout: 60000, // 60 秒,适合大文件
1653
1614
  });
1654
1615
 
1655
1616
  // 或在运行时修改
1656
1617
  request.setParams({
1657
- downloadTimeout: 120000, // 120 秒
1618
+ downloadTimeout: 120000, // 120 秒
1658
1619
  });
1659
1620
  ```
1660
1621
 
@@ -1670,18 +1631,13 @@ A: 目前库返回的是 Promise,无法直接获取 DownloadTask 来调用 abo
1670
1631
  // 推荐:在进度回调中检查状态
1671
1632
  let shouldCancel = false;
1672
1633
 
1673
- await request.downloadFile(
1674
- '/file.zip',
1675
- undefined,
1676
- undefined,
1677
- (progress) => {
1678
- if (shouldCancel) {
1679
- // 无法直接中止,但可以停止处理
1680
- return;
1681
- }
1682
- console.log(`下载进度: ${progress.progress}%`);
1634
+ await request.downloadFile('/file.zip', undefined, undefined, (progress) => {
1635
+ if (shouldCancel) {
1636
+ // 无法直接中止,但可以停止处理
1637
+ return;
1683
1638
  }
1684
- );
1639
+ console.log(`下载进度: ${progress.progress}%`);
1640
+ });
1685
1641
 
1686
1642
  // 用户点击取消按钮时
1687
1643
  function cancelDownload() {
@@ -1723,19 +1679,20 @@ A: 设置 `maxRetryCount` 为 0。
1723
1679
 
1724
1680
  ```typescript
1725
1681
  const request = new UniRequest({
1726
- maxRetryCount: 0, // 禁用重试
1682
+ maxRetryCount: 0, // 禁用重试
1727
1683
  });
1728
1684
  ```
1729
1685
 
1730
1686
  ### Q: headerProcessor 每次都会执行吗?
1731
1687
 
1732
- A: 是的,包括重试时都会执行。这是为了确保每次请求都获得最新的 header 值(如动态生成的签名)。
1688
+ A: 每次调用 `request` / `get` / `post` 等接口时都会执行一次;如果该次请求触发自动重试,会复用第一次处理后的 header,不会重复执行。这可以保证同一次业务请求在重试时复用同一个幂等 header。唯一例外是 `403` 刷新 token 成功后,SDK 会基于新 token 重新生成一次 header,再立即重试。
1733
1689
 
1734
1690
  ## 版本历史
1735
1691
 
1736
1692
  ### v1.6.2(最新)
1737
1693
 
1738
1694
  - ✨ **新增** 文件下载功能 (`downloadFile` 方法)
1695
+
1739
1696
  - 支持下载文件到本地目录或临时目录
1740
1697
  - 支持实时下载进度监听
1741
1698
  - 支持自定义请求头
@@ -1743,12 +1700,14 @@ A: 是的,包括重试时都会执行。这是为了确保每次请求都获
1743
1700
  - 默认超时时间 30 秒(可配置)
1744
1701
 
1745
1702
  - 📝 **文档改进** 完整的 JSDoc 注释
1703
+
1746
1704
  - 所有公开/私有方法添加详细 JSDoc
1747
1705
  - 所有参数和返回值有清晰说明
1748
1706
  - 关键方法包含使用示例
1749
1707
  - 100% 注释覆盖率
1750
1708
 
1751
1709
  - 📚 **README 更新**
1710
+
1752
1711
  - 新增《文件下载》文档章节
1753
1712
  - 新增文件下载的详细使用示例(示例 4-2、4-3)
1754
1713
  - 更新目录索引
package/dist/index.esm.js CHANGED
@@ -588,43 +588,49 @@ var UniRequest = /*#__PURE__*/function () {
588
588
  /** 添加日志上报的小程序username */
589
589
  'X-Request-Username': this.username
590
590
  });
591
+ var finalHeader;
591
592
  var requestedToken = false;
592
593
  var retryCount = 0;
593
594
  return new Promise(function (res, rej) {
594
595
  var retryFucntion = /*#__PURE__*/function () {
595
596
  var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
596
- var finalHeader, processedHeader;
597
+ var requestHeader, processedHeader;
597
598
  return _regeneratorRuntime().wrap(function _callee$(_context) {
598
599
  while (1) switch (_context.prev = _context.next) {
599
600
  case 0:
601
+ if (finalHeader) {
602
+ _context.next = 17;
603
+ break;
604
+ }
605
+ requestHeader = Object.assign({}, header);
600
606
  /** 因为token需要动态获取,因此放入这里 */
601
- header[_this3.tokenHeader] = "".concat(_this3.tokenPrefix).concat(_this3.token);
607
+ requestHeader[_this3.tokenHeader] = "".concat(_this3.tokenPrefix).concat(_this3.token);
602
608
  /** 针对h5调试的时候,因为塞入cookie是不安全的,因此无需塞入cookie,使用登录页后,后端塞入的cookie */
603
609
  // #ifdef H5
604
610
  if (_this3.tokenHeader === 'cookie') {
605
- delete header[_this3.tokenHeader];
611
+ delete requestHeader[_this3.tokenHeader];
606
612
  }
607
613
  // #endif
608
- /** 执行请求头预处理 */
609
- finalHeader = header;
614
+ /** 单次请求生命周期内只执行一次请求头预处理,重试复用同一份结果 */
615
+ finalHeader = requestHeader;
610
616
  if (!_this3.headerProcessor) {
611
- _context.next = 15;
617
+ _context.next = 17;
612
618
  break;
613
619
  }
614
- _context.prev = 4;
615
- _context.next = 7;
616
- return _this3.headerProcessor(header);
617
- case 7:
620
+ _context.prev = 6;
621
+ _context.next = 9;
622
+ return _this3.headerProcessor(requestHeader);
623
+ case 9:
618
624
  processedHeader = _context.sent;
619
- finalHeader = Object.assign(Object.assign({}, header), processedHeader);
620
- _context.next = 15;
625
+ finalHeader = Object.assign(Object.assign({}, requestHeader), processedHeader);
626
+ _context.next = 17;
621
627
  break;
622
- case 11:
623
- _context.prev = 11;
624
- _context.t0 = _context["catch"](4);
628
+ case 13:
629
+ _context.prev = 13;
630
+ _context.t0 = _context["catch"](6);
625
631
  _this3.rejectHandler(rej, _context.t0);
626
632
  return _context.abrupt("return");
627
- case 15:
633
+ case 17:
628
634
  callbackPromise(Object.assign(Object.assign({}, params), {
629
635
  header: finalHeader,
630
636
  url: url,
@@ -657,6 +663,7 @@ var UniRequest = /*#__PURE__*/function () {
657
663
  (_this3.getTokenFun ? _this3.getTokenFun() : getToken(_this3.tokenEventName)).then(function (token) {
658
664
  _this3.token = token;
659
665
  requestedToken = true;
666
+ finalHeader = void 0;
660
667
  /** 获取token应该立马请求,不需要延迟 */
661
668
  retryFucntion();
662
669
  })["catch"](function () {
@@ -690,11 +697,11 @@ var UniRequest = /*#__PURE__*/function () {
690
697
  _this3.rejectHandler(rej, rejData);
691
698
  }
692
699
  });
693
- case 16:
700
+ case 18:
694
701
  case "end":
695
702
  return _context.stop();
696
703
  }
697
- }, _callee, null, [[4, 11]]);
704
+ }, _callee, null, [[6, 13]]);
698
705
  }));
699
706
  return function retryFucntion() {
700
707
  return _ref.apply(this, arguments);
package/dist/index.umd.js CHANGED
@@ -594,43 +594,49 @@
594
594
  /** 添加日志上报的小程序username */
595
595
  'X-Request-Username': this.username
596
596
  });
597
+ var finalHeader;
597
598
  var requestedToken = false;
598
599
  var retryCount = 0;
599
600
  return new Promise(function (res, rej) {
600
601
  var retryFucntion = /*#__PURE__*/function () {
601
602
  var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
602
- var finalHeader, processedHeader;
603
+ var requestHeader, processedHeader;
603
604
  return _regeneratorRuntime().wrap(function _callee$(_context) {
604
605
  while (1) switch (_context.prev = _context.next) {
605
606
  case 0:
607
+ if (finalHeader) {
608
+ _context.next = 17;
609
+ break;
610
+ }
611
+ requestHeader = Object.assign({}, header);
606
612
  /** 因为token需要动态获取,因此放入这里 */
607
- header[_this3.tokenHeader] = "".concat(_this3.tokenPrefix).concat(_this3.token);
613
+ requestHeader[_this3.tokenHeader] = "".concat(_this3.tokenPrefix).concat(_this3.token);
608
614
  /** 针对h5调试的时候,因为塞入cookie是不安全的,因此无需塞入cookie,使用登录页后,后端塞入的cookie */
609
615
  // #ifdef H5
610
616
  if (_this3.tokenHeader === 'cookie') {
611
- delete header[_this3.tokenHeader];
617
+ delete requestHeader[_this3.tokenHeader];
612
618
  }
613
619
  // #endif
614
- /** 执行请求头预处理 */
615
- finalHeader = header;
620
+ /** 单次请求生命周期内只执行一次请求头预处理,重试复用同一份结果 */
621
+ finalHeader = requestHeader;
616
622
  if (!_this3.headerProcessor) {
617
- _context.next = 15;
623
+ _context.next = 17;
618
624
  break;
619
625
  }
620
- _context.prev = 4;
621
- _context.next = 7;
622
- return _this3.headerProcessor(header);
623
- case 7:
626
+ _context.prev = 6;
627
+ _context.next = 9;
628
+ return _this3.headerProcessor(requestHeader);
629
+ case 9:
624
630
  processedHeader = _context.sent;
625
- finalHeader = Object.assign(Object.assign({}, header), processedHeader);
626
- _context.next = 15;
631
+ finalHeader = Object.assign(Object.assign({}, requestHeader), processedHeader);
632
+ _context.next = 17;
627
633
  break;
628
- case 11:
629
- _context.prev = 11;
630
- _context.t0 = _context["catch"](4);
634
+ case 13:
635
+ _context.prev = 13;
636
+ _context.t0 = _context["catch"](6);
631
637
  _this3.rejectHandler(rej, _context.t0);
632
638
  return _context.abrupt("return");
633
- case 15:
639
+ case 17:
634
640
  callbackPromise(Object.assign(Object.assign({}, params), {
635
641
  header: finalHeader,
636
642
  url: url,
@@ -663,6 +669,7 @@
663
669
  (_this3.getTokenFun ? _this3.getTokenFun() : getToken(_this3.tokenEventName)).then(function (token) {
664
670
  _this3.token = token;
665
671
  requestedToken = true;
672
+ finalHeader = void 0;
666
673
  /** 获取token应该立马请求,不需要延迟 */
667
674
  retryFucntion();
668
675
  })["catch"](function () {
@@ -696,11 +703,11 @@
696
703
  _this3.rejectHandler(rej, rejData);
697
704
  }
698
705
  });
699
- case 16:
706
+ case 18:
700
707
  case "end":
701
708
  return _context.stop();
702
709
  }
703
- }, _callee, null, [[4, 11]]);
710
+ }, _callee, null, [[6, 13]]);
704
711
  }));
705
712
  return function retryFucntion() {
706
713
  return _ref.apply(this, arguments);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uniapp-request-sdk",
3
- "version": "1.7.0",
3
+ "version": "1.7.1",
4
4
  "description": "用于uniapp小程序的请求库的sdk",
5
5
  "main": "dist/index.umd.js",
6
6
  "module": " dist/index.esm.js",