request-iframe 0.0.3 → 0.0.5
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/QUICKSTART.CN.md +35 -8
- package/QUICKSTART.md +35 -8
- package/README.CN.md +177 -24
- package/README.md +237 -19
- package/library/__tests__/channel.test.ts +16 -4
- package/library/__tests__/coverage-branches.test.ts +356 -0
- package/library/__tests__/debug.test.ts +22 -0
- package/library/__tests__/dispatcher.test.ts +8 -4
- package/library/__tests__/requestIframe.test.ts +1243 -87
- package/library/__tests__/stream.test.ts +92 -16
- package/library/__tests__/utils.test.ts +41 -1
- package/library/api/client.d.ts.map +1 -1
- package/library/api/client.js +1 -0
- package/library/constants/index.d.ts +2 -0
- package/library/constants/index.d.ts.map +1 -1
- package/library/constants/index.js +3 -1
- package/library/constants/messages.d.ts +3 -0
- package/library/constants/messages.d.ts.map +1 -1
- package/library/constants/messages.js +3 -0
- package/library/core/client-server.d.ts +4 -0
- package/library/core/client-server.d.ts.map +1 -1
- package/library/core/client-server.js +45 -22
- package/library/core/client.d.ts +36 -4
- package/library/core/client.d.ts.map +1 -1
- package/library/core/client.js +508 -285
- package/library/core/request.d.ts +3 -1
- package/library/core/request.d.ts.map +1 -1
- package/library/core/request.js +2 -1
- package/library/core/response.d.ts +26 -4
- package/library/core/response.d.ts.map +1 -1
- package/library/core/response.js +192 -112
- package/library/core/server.d.ts +13 -0
- package/library/core/server.d.ts.map +1 -1
- package/library/core/server.js +221 -6
- package/library/index.d.ts +2 -1
- package/library/index.d.ts.map +1 -1
- package/library/index.js +39 -3
- package/library/message/channel.d.ts +2 -2
- package/library/message/channel.d.ts.map +1 -1
- package/library/message/channel.js +5 -1
- package/library/message/dispatcher.d.ts +2 -2
- package/library/message/dispatcher.d.ts.map +1 -1
- package/library/message/dispatcher.js +6 -5
- package/library/stream/index.d.ts +11 -1
- package/library/stream/index.d.ts.map +1 -1
- package/library/stream/index.js +21 -3
- package/library/stream/types.d.ts +2 -2
- package/library/stream/types.d.ts.map +1 -1
- package/library/stream/writable-stream.d.ts +1 -1
- package/library/stream/writable-stream.d.ts.map +1 -1
- package/library/stream/writable-stream.js +87 -47
- package/library/types/index.d.ts +29 -5
- package/library/types/index.d.ts.map +1 -1
- package/library/utils/debug.d.ts.map +1 -1
- package/library/utils/debug.js +6 -2
- package/library/utils/error.d.ts +21 -0
- package/library/utils/error.d.ts.map +1 -0
- package/library/utils/error.js +34 -0
- package/library/utils/index.d.ts +21 -0
- package/library/utils/index.d.ts.map +1 -1
- package/library/utils/index.js +141 -2
- package/library/utils/path-match.d.ts +16 -0
- package/library/utils/path-match.d.ts.map +1 -1
- package/library/utils/path-match.js +65 -0
- package/package.json +2 -1
- package/react/library/__tests__/index.test.tsx +44 -22
- package/react/library/index.d.ts.map +1 -1
- package/react/library/index.js +81 -23
- package/react/package.json +7 -0
package/QUICKSTART.CN.md
CHANGED
|
@@ -172,22 +172,49 @@ server.on('/api/download', async (req, res) => {
|
|
|
172
172
|
|
|
173
173
|
// Client 端
|
|
174
174
|
const response = await client.send('/api/download', {});
|
|
175
|
-
if (response.
|
|
175
|
+
if (response.data instanceof File || response.data instanceof Blob) {
|
|
176
|
+
const file = response.data instanceof File ? response.data : null;
|
|
177
|
+
const fileName = file?.name || 'download';
|
|
178
|
+
|
|
176
179
|
// 创建下载链接
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
{ type: response.fileData.mimeType }
|
|
180
|
-
);
|
|
181
|
-
const url = URL.createObjectURL(blob);
|
|
182
|
-
|
|
180
|
+
const url = URL.createObjectURL(response.data);
|
|
181
|
+
|
|
183
182
|
// 触发下载
|
|
184
183
|
const a = document.createElement('a');
|
|
185
184
|
a.href = url;
|
|
186
|
-
a.download =
|
|
185
|
+
a.download = fileName;
|
|
187
186
|
a.click();
|
|
187
|
+
URL.revokeObjectURL(url);
|
|
188
188
|
}
|
|
189
189
|
```
|
|
190
190
|
|
|
191
|
+
### 文件上传(Client → Server)
|
|
192
|
+
|
|
193
|
+
Client 向 Server 发送文件仅走**流式**。默认 `autoResolve: true`,Server 会在进入 handler 前把文件解析成 `File/Blob` 放到 `req.body`。
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
// Client 端
|
|
197
|
+
const file = new File(['Hello Upload'], 'upload.txt', { type: 'text/plain' });
|
|
198
|
+
await client.send('/api/upload', file); // File/Blob 会自动分发到 sendFile(走流式)
|
|
199
|
+
|
|
200
|
+
// Server 端
|
|
201
|
+
server.on('/api/upload', async (req, res) => {
|
|
202
|
+
const blob = req.body as Blob;
|
|
203
|
+
const text = await blob.text();
|
|
204
|
+
res.send({ ok: true, text });
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### 路由参数(req.params)
|
|
209
|
+
|
|
210
|
+
支持 Express 风格的 `:param` 路由参数,解析结果在 `req.params`。
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
server.on('/api/users/:id', (req, res) => {
|
|
214
|
+
res.send({ userId: req.params.id });
|
|
215
|
+
});
|
|
216
|
+
```
|
|
217
|
+
|
|
191
218
|
### 调试模式
|
|
192
219
|
|
|
193
220
|
开启 trace 模式查看详细日志:
|
package/QUICKSTART.md
CHANGED
|
@@ -172,22 +172,49 @@ server.on('/api/download', async (req, res) => {
|
|
|
172
172
|
|
|
173
173
|
// Client side
|
|
174
174
|
const response = await client.send('/api/download', {});
|
|
175
|
-
if (response.
|
|
175
|
+
if (response.data instanceof File || response.data instanceof Blob) {
|
|
176
|
+
const file = response.data instanceof File ? response.data : null;
|
|
177
|
+
const fileName = file?.name || 'download';
|
|
178
|
+
|
|
176
179
|
// Create download link
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
{ type: response.fileData.mimeType }
|
|
180
|
-
);
|
|
181
|
-
const url = URL.createObjectURL(blob);
|
|
182
|
-
|
|
180
|
+
const url = URL.createObjectURL(response.data);
|
|
181
|
+
|
|
183
182
|
// Trigger download
|
|
184
183
|
const a = document.createElement('a');
|
|
185
184
|
a.href = url;
|
|
186
|
-
a.download =
|
|
185
|
+
a.download = fileName;
|
|
187
186
|
a.click();
|
|
187
|
+
URL.revokeObjectURL(url);
|
|
188
188
|
}
|
|
189
189
|
```
|
|
190
190
|
|
|
191
|
+
### File Upload (Client → Server)
|
|
192
|
+
|
|
193
|
+
Client sends files via stream only. By default `autoResolve: true`, server will resolve the file to `File/Blob` and put it into `req.body` before calling your handler.
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
// Client side
|
|
197
|
+
const file = new File(['Hello Upload'], 'upload.txt', { type: 'text/plain' });
|
|
198
|
+
await client.send('/api/upload', file); // File/Blob auto-dispatches to sendFile (stream)
|
|
199
|
+
|
|
200
|
+
// Server side
|
|
201
|
+
server.on('/api/upload', async (req, res) => {
|
|
202
|
+
const blob = req.body as Blob;
|
|
203
|
+
const text = await blob.text();
|
|
204
|
+
res.send({ ok: true, text });
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Route Params (req.params)
|
|
209
|
+
|
|
210
|
+
Supports Express-style `:param` route parameters. Extracted parameters are available in `req.params`.
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
server.on('/api/users/:id', (req, res) => {
|
|
214
|
+
res.send({ userId: req.params.id });
|
|
215
|
+
});
|
|
216
|
+
```
|
|
217
|
+
|
|
191
218
|
### Debug Mode
|
|
192
219
|
|
|
193
220
|
Enable trace mode to view detailed logs:
|
package/README.CN.md
CHANGED
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
- ⏱️ **智能超时** - 三阶段超时(连接/同步/异步),自动识别长任务
|
|
75
75
|
- 📦 **TypeScript** - 完整的类型定义和智能提示
|
|
76
76
|
- 🔒 **消息隔离** - secretKey 机制避免多实例消息串线
|
|
77
|
-
- 📁 **文件传输** -
|
|
77
|
+
- 📁 **文件传输** - 支持文件通过流方式传输(Client→Server)
|
|
78
78
|
- 🌊 **流式传输** - 支持大文件分块传输,支持异步迭代器
|
|
79
79
|
- 🌍 **多语言** - 错误消息可自定义,便于国际化
|
|
80
80
|
- ✅ **协议版本** - 内置版本控制,便于升级兼容
|
|
@@ -252,7 +252,8 @@ client.send('/api/getData', data, {
|
|
|
252
252
|
const client = requestIframeClient(iframe, { secretKey: 'main-app' });
|
|
253
253
|
|
|
254
254
|
// 获取子应用的用户信息
|
|
255
|
-
const
|
|
255
|
+
const userInfoResponse = await client.send('/api/user/info', {});
|
|
256
|
+
console.log(userInfoResponse.data); // 用户信息数据
|
|
256
257
|
|
|
257
258
|
// 通知子应用更新数据
|
|
258
259
|
await client.send('/api/data/refresh', { timestamp: Date.now() });
|
|
@@ -296,7 +297,8 @@ server.on('/api/data', async (req, res) => {
|
|
|
296
297
|
|
|
297
298
|
// 父页面(跨域)
|
|
298
299
|
const client = requestIframeClient(iframe, { secretKey: 'data-api' });
|
|
299
|
-
const
|
|
300
|
+
const response = await client.send('/api/data', {});
|
|
301
|
+
const data = response.data; // 成功获取跨域数据
|
|
300
302
|
```
|
|
301
303
|
|
|
302
304
|
### 文件预览和下载
|
|
@@ -318,8 +320,8 @@ server.on('/api/processFile', async (req, res) => {
|
|
|
318
320
|
|
|
319
321
|
// 父页面:下载文件
|
|
320
322
|
const response = await client.send('/api/processFile', { fileId: '123' });
|
|
321
|
-
if (response.
|
|
322
|
-
downloadFile(response.
|
|
323
|
+
if (response.data instanceof File || response.data instanceof Blob) {
|
|
324
|
+
downloadFile(response.data);
|
|
323
325
|
}
|
|
324
326
|
```
|
|
325
327
|
|
|
@@ -439,6 +441,13 @@ server.use(['/user', '/profile'], (req, res, next) => {
|
|
|
439
441
|
|
|
440
442
|
request-iframe 模拟了 HTTP 的 cookie 自动管理机制:
|
|
441
443
|
|
|
444
|
+
**Cookie 有效期与生命周期(重要):**
|
|
445
|
+
|
|
446
|
+
- **仅内存存储**:cookies 存在于 Client 实例内部的 `CookieStore`(不会写入浏览器真实 Cookie)。
|
|
447
|
+
- **生命周期**:默认从 `requestIframeClient()` 创建开始,直到 `client.destroy()` 为止。
|
|
448
|
+
- **`open()` / `close()`**:只控制消息监听的开启/关闭,**不会清空**内部 cookies。
|
|
449
|
+
- **过期处理**:会遵循 `Expires` / `Max-Age`。已过期的 cookie 在读取/发送时会被自动过滤(也可以用 `client.clearCookies()` / `client.removeCookie()` 手动清理)。
|
|
450
|
+
|
|
442
451
|
```
|
|
443
452
|
┌─────────────────────────────────────────────────────────────────┐
|
|
444
453
|
│ Cookies 自动管理流程 │
|
|
@@ -505,10 +514,12 @@ server.on('/api/logout', (req, res) => {
|
|
|
505
514
|
await client.send('/api/login', { username: 'tom', password: '123' });
|
|
506
515
|
|
|
507
516
|
// Client 端:后续请求 /api/getUserInfo(自动携带 authToken 和 userId)
|
|
508
|
-
const
|
|
517
|
+
const userInfoResponse = await client.send('/api/getUserInfo', {});
|
|
518
|
+
const userInfo = userInfoResponse.data;
|
|
509
519
|
|
|
510
520
|
// Client 端:请求根路径(只携带 userId,因为 authToken 的 path 是 /api)
|
|
511
|
-
const
|
|
521
|
+
const rootResponse = await client.send('/other', {});
|
|
522
|
+
const rootData = rootResponse.data;
|
|
512
523
|
```
|
|
513
524
|
|
|
514
525
|
#### Client Cookie 管理 API
|
|
@@ -583,22 +594,48 @@ server.on('/api/download', async (req, res) => {
|
|
|
583
594
|
|
|
584
595
|
// Client 端接收
|
|
585
596
|
const response = await client.send('/api/download', {});
|
|
586
|
-
if (response.
|
|
587
|
-
const
|
|
588
|
-
|
|
589
|
-
// content 是 base64 编码的字符串
|
|
590
|
-
const binaryString = atob(content);
|
|
591
|
-
const blob = new Blob([binaryString], { type: mimeType });
|
|
597
|
+
if (response.data instanceof File || response.data instanceof Blob) {
|
|
598
|
+
const file = response.data instanceof File ? response.data : null;
|
|
599
|
+
const fileName = file?.name || 'download';
|
|
592
600
|
|
|
593
|
-
// 下载文件
|
|
594
|
-
const url = URL.createObjectURL(
|
|
601
|
+
// 直接使用 File/Blob 下载文件
|
|
602
|
+
const url = URL.createObjectURL(response.data);
|
|
595
603
|
const a = document.createElement('a');
|
|
596
604
|
a.href = url;
|
|
597
|
-
a.download = fileName
|
|
605
|
+
a.download = fileName;
|
|
598
606
|
a.click();
|
|
607
|
+
URL.revokeObjectURL(url);
|
|
599
608
|
}
|
|
600
609
|
```
|
|
601
610
|
|
|
611
|
+
#### Client → Server(Client 向 Server 发送文件)
|
|
612
|
+
|
|
613
|
+
Client 端发送文件**仅走流式**。使用 `sendFile()`(或直接 `send(path, file)`);Server 端在 `autoResolve: true`(默认)时会把文件自动解析成 `File/Blob` 放到 `req.body`,当 `autoResolve: false` 时则通过 `req.stream` / `req.body` 暴露为 `IframeFileReadableStream`。
|
|
614
|
+
|
|
615
|
+
```typescript
|
|
616
|
+
// Client 端:发送文件(stream,autoResolve 默认 true)
|
|
617
|
+
const file = new File(['Hello Upload'], 'upload.txt', { type: 'text/plain' });
|
|
618
|
+
const response = await client.send('/api/upload', file);
|
|
619
|
+
|
|
620
|
+
// 或显式使用 sendFile
|
|
621
|
+
const blob = new Blob(['binary data'], { type: 'application/octet-stream' });
|
|
622
|
+
const response2 = await client.sendFile('/api/upload', blob, {
|
|
623
|
+
fileName: 'data.bin',
|
|
624
|
+
mimeType: 'application/octet-stream',
|
|
625
|
+
autoResolve: true // 可选,默认 true:Server 在 req.body 里拿到 File/Blob
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
// Server 端:接收文件(autoResolve true → req.body 是 File/Blob)
|
|
629
|
+
server.on('/api/upload', async (req, res) => {
|
|
630
|
+
const blob = req.body as Blob; // 如果 client 发送的是 File,这里也可能是 File
|
|
631
|
+
const text = await blob.text();
|
|
632
|
+
console.log('Received file content:', text);
|
|
633
|
+
res.send({ success: true, size: blob.size });
|
|
634
|
+
});
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
**提示**:当 `client.send()` 的 `body` 是 `File/Blob` 时,会自动分发到 `client.sendFile()`(走流式)。`autoResolve` 为 true(默认)时 Server 拿到 `req.body`(File/Blob),为 false 时拿到 `req.stream` / `req.body`(`IframeFileReadableStream`)。
|
|
638
|
+
|
|
602
639
|
### 流式传输(Stream)
|
|
603
640
|
|
|
604
641
|
对于大文件或需要分块传输的场景,可以使用流式传输:
|
|
@@ -608,7 +645,7 @@ import {
|
|
|
608
645
|
IframeWritableStream,
|
|
609
646
|
IframeFileWritableStream,
|
|
610
647
|
isIframeReadableStream,
|
|
611
|
-
|
|
648
|
+
isIframeFileReadableStream
|
|
612
649
|
} from 'request-iframe';
|
|
613
650
|
|
|
614
651
|
// Server 端:使用迭代器发送数据流
|
|
@@ -692,7 +729,7 @@ if (isIframeReadableStream(response.stream)) {
|
|
|
692
729
|
// Client 端:接收文件流
|
|
693
730
|
const fileResponse = await client.send('/api/fileStream', {});
|
|
694
731
|
|
|
695
|
-
if (
|
|
732
|
+
if (isIframeFileReadableStream(fileResponse.stream)) {
|
|
696
733
|
// 读取为 Blob
|
|
697
734
|
const blob = await fileResponse.stream.readAsBlob();
|
|
698
735
|
|
|
@@ -714,9 +751,9 @@ if (isIframeFileStream(fileResponse.stream)) {
|
|
|
714
751
|
| 类型 | 说明 |
|
|
715
752
|
|------|------|
|
|
716
753
|
| `IframeWritableStream` | 服务端可写流,用于发送普通数据 |
|
|
717
|
-
| `IframeFileWritableStream` |
|
|
754
|
+
| `IframeFileWritableStream` | 服务端文件可写流(文件流) |
|
|
718
755
|
| `IframeReadableStream` | 客户端可读流,用于接收普通数据 |
|
|
719
|
-
| `IframeFileReadableStream` |
|
|
756
|
+
| `IframeFileReadableStream` | 客户端文件可读流(文件流) |
|
|
720
757
|
|
|
721
758
|
**流选项:**
|
|
722
759
|
|
|
@@ -852,14 +889,16 @@ await client.send('/api/longTask', {}, {
|
|
|
852
889
|
|
|
853
890
|
#### client.send(path, body?, options?)
|
|
854
891
|
|
|
855
|
-
|
|
892
|
+
发送请求。会根据 body 类型自动分发到 `sendFile()` 或 `sendStream()`:
|
|
893
|
+
- `File/Blob` → `sendFile()`
|
|
894
|
+
- `IframeWritableStream` → `sendStream()`
|
|
856
895
|
|
|
857
896
|
**参数:**
|
|
858
897
|
|
|
859
898
|
| 参数 | 类型 | 说明 |
|
|
860
899
|
|------|------|------|
|
|
861
900
|
| `path` | `string` | 请求路径 |
|
|
862
|
-
| `body` | `
|
|
901
|
+
| `body` | `any` | 请求数据(可选)。可以是普通对象、File、Blob、或 IframeWritableStream;会自动分发:File/Blob → `sendFile()`,IframeWritableStream → `sendStream()` |
|
|
863
902
|
| `options.ackTimeout` | `number` | ACK 确认超时(ms),默认 1000 |
|
|
864
903
|
| `options.timeout` | `number` | 请求超时(ms),默认 5000 |
|
|
865
904
|
| `options.asyncTimeout` | `number` | 异步超时(ms),默认 120000 |
|
|
@@ -880,6 +919,69 @@ interface Response<T = any> {
|
|
|
880
919
|
}
|
|
881
920
|
```
|
|
882
921
|
|
|
922
|
+
**示例:**
|
|
923
|
+
|
|
924
|
+
```typescript
|
|
925
|
+
// 发送普通对象(自动 Content-Type: application/json)
|
|
926
|
+
await client.send('/api/data', { name: 'test' });
|
|
927
|
+
|
|
928
|
+
// 发送字符串(自动 Content-Type: text/plain)
|
|
929
|
+
await client.send('/api/text', 'Hello');
|
|
930
|
+
|
|
931
|
+
// 发送 File/Blob(自动分发到 sendFile)
|
|
932
|
+
const file = new File(['content'], 'test.txt');
|
|
933
|
+
await client.send('/api/upload', file);
|
|
934
|
+
|
|
935
|
+
// 发送流(自动分发到 sendStream)
|
|
936
|
+
const stream = new IframeWritableStream({ iterator: async function* () { yield 'data'; } });
|
|
937
|
+
await client.send('/api/uploadStream', stream);
|
|
938
|
+
```
|
|
939
|
+
|
|
940
|
+
#### client.sendFile(path, content, options?)
|
|
941
|
+
|
|
942
|
+
发送文件作为请求体(通过流传输;当 `autoResolve` 为 true 时,Server 在 `req.body` 中拿到 File/Blob)。
|
|
943
|
+
|
|
944
|
+
**参数:**
|
|
945
|
+
|
|
946
|
+
| 参数 | 类型 | 说明 |
|
|
947
|
+
|------|------|------|
|
|
948
|
+
| `path` | `string` | 请求路径 |
|
|
949
|
+
| `content` | `string \| Blob \| File` | 文件内容 |
|
|
950
|
+
| `options.mimeType` | `string` | MIME 类型(可选,优先使用 content.type) |
|
|
951
|
+
| `options.fileName` | `string` | 文件名(可选) |
|
|
952
|
+
| `options.autoResolve` | `boolean` | 为 true(默认)时 Server 在 `req.body` 中拿到 File/Blob;为 false 时 Server 在 `req.stream` / `req.body` 中拿到 `IframeFileReadableStream` |
|
|
953
|
+
| `options.ackTimeout` | `number` | ACK 确认超时(ms),默认 1000 |
|
|
954
|
+
| `options.timeout` | `number` | 请求超时(ms),默认 5000 |
|
|
955
|
+
| `options.asyncTimeout` | `number` | 异步超时(ms),默认 120000 |
|
|
956
|
+
| `options.headers` | `object` | 请求 headers(可选) |
|
|
957
|
+
| `options.cookies` | `object` | 请求 cookies(可选) |
|
|
958
|
+
| `options.requestId` | `string` | 自定义请求 ID(可选) |
|
|
959
|
+
|
|
960
|
+
**返回值:** `Promise<Response>`
|
|
961
|
+
|
|
962
|
+
**说明:** 文件通过流发送。`autoResolve` 为 true(默认)时 Server 收到 `req.body`(File/Blob);为 false 时 Server 收到 `req.stream` / `req.body`(`IframeFileReadableStream`)。
|
|
963
|
+
|
|
964
|
+
#### client.sendStream(path, stream, options?)
|
|
965
|
+
|
|
966
|
+
发送流作为请求体(Server 端收到可读流)。
|
|
967
|
+
|
|
968
|
+
**参数:**
|
|
969
|
+
|
|
970
|
+
| 参数 | 类型 | 说明 |
|
|
971
|
+
|------|------|------|
|
|
972
|
+
| `path` | `string` | 请求路径 |
|
|
973
|
+
| `stream` | `IframeWritableStream` | 要发送的可写流 |
|
|
974
|
+
| `options.ackTimeout` | `number` | ACK 确认超时(ms),默认 1000 |
|
|
975
|
+
| `options.timeout` | `number` | 请求超时(ms),默认 5000 |
|
|
976
|
+
| `options.asyncTimeout` | `number` | 异步超时(ms),默认 120000 |
|
|
977
|
+
| `options.headers` | `object` | 请求 headers(可选) |
|
|
978
|
+
| `options.cookies` | `object` | 请求 cookies(可选) |
|
|
979
|
+
| `options.requestId` | `string` | 自定义请求 ID(可选) |
|
|
980
|
+
|
|
981
|
+
**返回值:** `Promise<Response>`
|
|
982
|
+
|
|
983
|
+
**说明:** Server 端的流在 `req.stream`(`IIframeReadableStream`)中,可用 `for await (const chunk of req.stream)` 迭代读取。
|
|
984
|
+
|
|
883
985
|
#### client.isConnect()
|
|
884
986
|
|
|
885
987
|
检测 Server 是否可达。
|
|
@@ -952,6 +1054,39 @@ client.interceptors.response.use(onFulfilled, onRejected?);
|
|
|
952
1054
|
type ServerHandler = (req: ServerRequest, res: ServerResponse) => any | Promise<any>;
|
|
953
1055
|
```
|
|
954
1056
|
|
|
1057
|
+
**ServerRequest 接口:**
|
|
1058
|
+
|
|
1059
|
+
```typescript
|
|
1060
|
+
interface ServerRequest {
|
|
1061
|
+
body: any; // 请求 body(普通数据;或 autoResolve=true 时的 File/Blob)
|
|
1062
|
+
stream?: IIframeReadableStream; // 请求流(sendStream;或 sendFile 且 autoResolve=false)
|
|
1063
|
+
headers: Record<string, string>; // 请求 headers
|
|
1064
|
+
cookies: Record<string, string>; // 请求 cookies
|
|
1065
|
+
path: string; // 请求路径(实际请求路径)
|
|
1066
|
+
params: Record<string, string>; // 路由参数(由 server.on 注册的路径模式解析得出,如 /api/users/:id)
|
|
1067
|
+
requestId: string; // 请求 ID
|
|
1068
|
+
origin: string; // 来源 origin
|
|
1069
|
+
source: Window; // 来源 window
|
|
1070
|
+
res: ServerResponse; // 关联的 Response 对象
|
|
1071
|
+
}
|
|
1072
|
+
```
|
|
1073
|
+
|
|
1074
|
+
**说明:**
|
|
1075
|
+
- Client 通过 `sendFile()`(或 `send(path, file)`)发送文件时:文件通过流传输;`autoResolve` 为 true(默认)时 Server 在 `req.body` 中拿到 File/Blob;为 false 时在 `req.stream` / `req.body` 中拿到 `IframeFileReadableStream`。
|
|
1076
|
+
- Client 通过 `sendStream()` 发送流时:Server 在 `req.stream` 中拿到 `IIframeReadableStream`,可用 `for await` 迭代读取。
|
|
1077
|
+
- **路径参数(类似 Express)**:支持 `/api/users/:id` 形式的路由参数,解析结果在 `req.params` 中。
|
|
1078
|
+
|
|
1079
|
+
```typescript
|
|
1080
|
+
server.on('/api/users/:id', (req, res) => {
|
|
1081
|
+
res.send({ userId: req.params.id });
|
|
1082
|
+
});
|
|
1083
|
+
|
|
1084
|
+
server.on('/api/users/:userId/posts/:postId', (req, res) => {
|
|
1085
|
+
const { userId, postId } = req.params;
|
|
1086
|
+
res.send({ userId, postId });
|
|
1087
|
+
});
|
|
1088
|
+
```
|
|
1089
|
+
|
|
955
1090
|
#### server.off(path)
|
|
956
1091
|
|
|
957
1092
|
移除路由处理器。
|
|
@@ -1245,10 +1380,12 @@ const IframeComponent = () => {
|
|
|
1245
1380
|
|
|
1246
1381
|
```typescript
|
|
1247
1382
|
interface ServerRequest {
|
|
1248
|
-
body: any; // 请求 body
|
|
1383
|
+
body: any; // 请求 body(普通数据;或 autoResolve=true 时的 File/Blob)
|
|
1384
|
+
stream?: IIframeReadableStream; // 请求流(sendStream;或 sendFile 且 autoResolve=false)
|
|
1249
1385
|
headers: Record<string, string>; // 请求 headers
|
|
1250
1386
|
cookies: Record<string, string>; // 请求 cookies
|
|
1251
|
-
path: string; //
|
|
1387
|
+
path: string; // 请求路径(实际请求路径)
|
|
1388
|
+
params: Record<string, string>; // 路由参数(由 server.on 注册的路径模式解析得出,如 /api/users/:id)
|
|
1252
1389
|
requestId: string; // 请求 ID
|
|
1253
1390
|
origin: string; // 来源 origin
|
|
1254
1391
|
source: Window; // 来源 window
|
|
@@ -1256,6 +1393,22 @@ interface ServerRequest {
|
|
|
1256
1393
|
}
|
|
1257
1394
|
```
|
|
1258
1395
|
|
|
1396
|
+
**路径参数(类似 Express)**:
|
|
1397
|
+
|
|
1398
|
+
支持使用 `:param` 形式声明路由参数,解析结果在 `req.params` 中。
|
|
1399
|
+
|
|
1400
|
+
```typescript
|
|
1401
|
+
server.on('/api/users/:id', (req, res) => {
|
|
1402
|
+
// 请求 /api/users/123 时:req.params.id === '123'
|
|
1403
|
+
res.send({ userId: req.params.id });
|
|
1404
|
+
});
|
|
1405
|
+
|
|
1406
|
+
server.on('/api/users/:userId/posts/:postId', (req, res) => {
|
|
1407
|
+
const { userId, postId } = req.params;
|
|
1408
|
+
res.send({ userId, postId });
|
|
1409
|
+
});
|
|
1410
|
+
```
|
|
1411
|
+
|
|
1259
1412
|
### ServerResponse 对象
|
|
1260
1413
|
|
|
1261
1414
|
```typescript
|