topbit 3.0.5 → 3.0.7
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/LICENSE +15 -122
- package/README.cn.md +924 -31
- package/README.md +880 -23
- package/demo/upserv.js +35 -0
- package/docs/README.md +16 -0
- package/docs/en/topbit-loader.md +305 -0
- package/docs/en/topbit-token.md +138 -0
- package/docs/topbit-loader.md +302 -0
- package/docs/topbit-token.md +168 -0
- package/images/topbit-middleware.webp +0 -0
- package/images/topbit.webp +0 -0
- package/package.json +1 -1
- package/src/bodyparser.js +1 -3
- package/src/extends/cookie.js +4 -5
- package/src/extends/jwt.js +2 -3
- package/src/extends/session.js +1 -1
- package/src/extends/timing.js +2 -2
- package/src/extends/tofile.js +2 -2
- package/src/token/token.js +2 -2
- package/src/topbit.js +4 -1
package/README.cn.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Topbit
|
|
4
4
|
|
|
5
|
-
[English Documentation](README.en.md)
|
|
5
|
+
#### [English Documentation](README.en.md)
|
|
6
6
|
|
|
7
7
|
topbit是基于Node.js的运行于服务端的Web框架,它没有任何第三方依赖,在独特的路由和中间件分组执行机制上进行了极致优化。
|
|
8
8
|
|
|
@@ -38,7 +38,7 @@ topbit是基于Node.js的运行于服务端的Web框架,它没有任何第三
|
|
|
38
38
|
* 默认设定和网络安全相关的配置,避免软件服务层面的DDOS攻击和其他网络安全问题。
|
|
39
39
|
|
|
40
40
|
|
|
41
|
-
##
|
|
41
|
+
## 📦安装
|
|
42
42
|
|
|
43
43
|
```javascript
|
|
44
44
|
npm i topbit
|
|
@@ -131,7 +131,7 @@ GET POST PUT PATCH DELETE OPTIONS TRACE HEAD
|
|
|
131
131
|
|
|
132
132
|
'use strict'
|
|
133
133
|
|
|
134
|
-
const Topbit = require('
|
|
134
|
+
const Topbit = require('topbit')
|
|
135
135
|
|
|
136
136
|
const app = new Topbit({
|
|
137
137
|
debug: true
|
|
@@ -176,7 +176,7 @@ app.run(8080)
|
|
|
176
176
|
``` JavaScript
|
|
177
177
|
'use strict';
|
|
178
178
|
|
|
179
|
-
const Topbit = require('topbit')
|
|
179
|
+
const Topbit = require('topbit')
|
|
180
180
|
|
|
181
181
|
let app = new Topbit({
|
|
182
182
|
debug: true
|
|
@@ -443,7 +443,7 @@ app.run(1234)
|
|
|
443
443
|
|
|
444
444
|
const Topbit = require('topbit')
|
|
445
445
|
//导入ToFile扩展
|
|
446
|
-
const {ToFile} =
|
|
446
|
+
const {ToFile} = Topbit.extensions
|
|
447
447
|
|
|
448
448
|
const app = new Topbit({
|
|
449
449
|
debug: true
|
|
@@ -509,7 +509,7 @@ app.run(1234)
|
|
|
509
509
|
|
|
510
510
|
const Topbit = require('topbit')
|
|
511
511
|
//导入ToFile扩展
|
|
512
|
-
const {ToFile} =
|
|
512
|
+
const {ToFile} = Topbit.extensions
|
|
513
513
|
|
|
514
514
|
const app = new Topbit({
|
|
515
515
|
debug: true
|
|
@@ -575,9 +575,6 @@ app.post('/upload', async c => {
|
|
|
575
575
|
|
|
576
576
|
let f = c.getFile('image')
|
|
577
577
|
|
|
578
|
-
//此函数是助手函数,makeName默认会按照时间戳生成名字,extName解析文件的扩展名。
|
|
579
|
-
//let fname = `${c.ext.makeName()}${c.ext.extName(f.filename)}`
|
|
580
|
-
|
|
581
578
|
//根据原始文件名解析扩展名并生成时间戳加随机数的唯一文件名。
|
|
582
579
|
|
|
583
580
|
let fname = c.ext.makeName(f.filename)
|
|
@@ -607,8 +604,6 @@ app.run(1234)
|
|
|
607
604
|
{
|
|
608
605
|
image : [
|
|
609
606
|
{
|
|
610
|
-
'content-type': CONTENT_TYPE,
|
|
611
|
-
//23.2.6以上可用,是content-type的别名,方便程序访问
|
|
612
607
|
type: CONTENT_TYPE,
|
|
613
608
|
filename: ORIGIN_FILENAME,
|
|
614
609
|
start : START,
|
|
@@ -622,8 +617,6 @@ app.run(1234)
|
|
|
622
617
|
|
|
623
618
|
video : [
|
|
624
619
|
{
|
|
625
|
-
'content-type': CONTENT_TYPE,
|
|
626
|
-
//23.2.6以上可用,是content-type的别名,方便程序访问
|
|
627
620
|
type: CONTENT_TYPE,
|
|
628
621
|
filename: ORIGIN_FILENAME,
|
|
629
622
|
start : START,
|
|
@@ -1056,18 +1049,18 @@ app.run(1234)
|
|
|
1056
1049
|
|
|
1057
1050
|
'use strict';
|
|
1058
1051
|
|
|
1059
|
-
const Topbit = require('topbit')
|
|
1052
|
+
const Topbit = require('topbit')
|
|
1060
1053
|
|
|
1061
1054
|
let app = new Topbit({
|
|
1062
1055
|
debug: true
|
|
1063
1056
|
});
|
|
1064
1057
|
|
|
1065
1058
|
//有则会覆盖,没有则添加。
|
|
1066
|
-
app.addService('name', 'first')
|
|
1059
|
+
app.addService('name', 'first')
|
|
1067
1060
|
app.addService('data', {
|
|
1068
1061
|
id : 123,
|
|
1069
1062
|
ip : '127.0.0.1'
|
|
1070
|
-
})
|
|
1063
|
+
})
|
|
1071
1064
|
|
|
1072
1065
|
/*
|
|
1073
1066
|
这可能看不出什么作用,毕竟在一个文件中,直接访问变量都可以,如果要做模块分离,就变得非常重要了。
|
|
@@ -1091,7 +1084,9 @@ app.run(1234)
|
|
|
1091
1084
|
|
|
1092
1085
|
```javascript
|
|
1093
1086
|
'use strict'
|
|
1087
|
+
|
|
1094
1088
|
const Topbit = require('topbit')
|
|
1089
|
+
|
|
1095
1090
|
const app = new Topbit({
|
|
1096
1091
|
debug: true
|
|
1097
1092
|
})
|
|
@@ -1221,7 +1216,7 @@ const app = new Topbit({
|
|
|
1221
1216
|
if (cluster.isMaster) {
|
|
1222
1217
|
app.setMsgEvent('test-msg', (worker, msg, handle) => {
|
|
1223
1218
|
//子进程中会通过message事件收到消息
|
|
1224
|
-
worker.
|
|
1219
|
+
worker.send({
|
|
1225
1220
|
id : worker.id,
|
|
1226
1221
|
data : 'ok'
|
|
1227
1222
|
})
|
|
@@ -1235,7 +1230,7 @@ if (cluster.isMaster) {
|
|
|
1235
1230
|
})
|
|
1236
1231
|
|
|
1237
1232
|
setIneterval(() => {
|
|
1238
|
-
process.
|
|
1233
|
+
process.send({
|
|
1239
1234
|
type : 'test-msg',
|
|
1240
1235
|
pid : process.pid,
|
|
1241
1236
|
time : (new Date()).toLocaleString()
|
|
@@ -1248,7 +1243,7 @@ if (cluster.isMaster) {
|
|
|
1248
1243
|
|
|
1249
1244
|
比较麻烦的地方在于,worker进程发送消息比较复杂,在22.4.0版本开始,提供了一个send方法用于快速发送消息。只有在worker进程中才会发送给master进程,所以不必额外进行worker进程检测。
|
|
1250
1245
|
|
|
1251
|
-
## app.
|
|
1246
|
+
## app.send 和 app.workerMsg
|
|
1252
1247
|
|
|
1253
1248
|
现在让我们来改写上面代码的worker进程发送消息的部分:
|
|
1254
1249
|
|
|
@@ -1264,7 +1259,7 @@ const app = new Topbit({
|
|
|
1264
1259
|
//master进程注册消息事件类型,worker进程不会执行。
|
|
1265
1260
|
app.setMsgEvent('test-msg', (worker, msg, handle) => {
|
|
1266
1261
|
//子进程中会通过message事件收到消息
|
|
1267
|
-
worker.
|
|
1262
|
+
worker.send({
|
|
1268
1263
|
id : worker.id,
|
|
1269
1264
|
data : 'ok'
|
|
1270
1265
|
})
|
|
@@ -1281,7 +1276,7 @@ cluster.isWorker
|
|
|
1281
1276
|
&&
|
|
1282
1277
|
setInterval(() => {
|
|
1283
1278
|
//只有worker会执行。
|
|
1284
|
-
app.
|
|
1279
|
+
app.send('test-msg', {
|
|
1285
1280
|
pid: process.pid,
|
|
1286
1281
|
time: (new Date).toLocaleString()
|
|
1287
1282
|
})
|
|
@@ -1322,7 +1317,7 @@ app.daemon(1234, 2)
|
|
|
1322
1317
|
|
|
1323
1318
|
----
|
|
1324
1319
|
|
|
1325
|
-
## strong模式
|
|
1320
|
+
## 🛡️strong模式
|
|
1326
1321
|
|
|
1327
1322
|
通过strong选项可以开启strong模式,此模式会监听uncaughtException和unhandledRejection事件,保证程序稳定运行。最简单的情况,你只需要给strong设置为true即可。
|
|
1328
1323
|
|
|
@@ -1382,7 +1377,7 @@ const app = new Topbit({
|
|
|
1382
1377
|
|
|
1383
1378
|
```
|
|
1384
1379
|
|
|
1385
|
-
##
|
|
1380
|
+
## 🔔同时运行http和https?
|
|
1386
1381
|
|
|
1387
1382
|
请注意这是打问号的,你最好不要在正式环境这样做,如果你已经开启了https,则不需要http,而且前端应用有些功能在不启用https是无法使用的。
|
|
1388
1383
|
|
|
@@ -1474,7 +1469,7 @@ app.daemon(1234, '0.0.0.0', 3)
|
|
|
1474
1469
|
|
|
1475
1470
|
- 这一切你都可以通过配置选项或是中间件来进行扩展和重写,既有限制也有自由。
|
|
1476
1471
|
|
|
1477
|
-
-
|
|
1472
|
+
- 它很快,并且我们一直在都在关注优化。
|
|
1478
1473
|
|
|
1479
1474
|
- 提供了一个sched函数用来快速设置cluster模式的调度方式,支持参数为'rr'或'none',本质就是设置cluster.schedulingPolicy的值。
|
|
1480
1475
|
|
|
@@ -1482,12 +1477,12 @@ app.daemon(1234, '0.0.0.0', 3)
|
|
|
1482
1477
|
框架在初始化会自动检测内存大小并设定相关上限,你可以在初始化后,通过更改secure中的属性来更改限制,这需要你使用daemon接口,也就是使用master管理子进程的模式。
|
|
1483
1478
|
|
|
1484
1479
|
|
|
1485
|
-
```
|
|
1480
|
+
```javascript
|
|
1486
1481
|
'use strict'
|
|
1487
1482
|
|
|
1488
|
-
const Topbit = require('topbit')
|
|
1483
|
+
const Topbit = require('topbit')
|
|
1489
1484
|
|
|
1490
|
-
let app = new Topbit()
|
|
1485
|
+
let app = new Topbit()
|
|
1491
1486
|
|
|
1492
1487
|
/*
|
|
1493
1488
|
以下操作可以通过选项memFactor控制,请参考上文的配置选项部分。
|
|
@@ -1505,13 +1500,911 @@ app.secure.diemem = 900_000_000;
|
|
|
1505
1500
|
app.secure.maxrss = 800_000_000;
|
|
1506
1501
|
|
|
1507
1502
|
app.get('/', async c => {
|
|
1508
|
-
c.to('ok')
|
|
1503
|
+
c.to('ok')
|
|
1504
|
+
})
|
|
1505
|
+
|
|
1506
|
+
app.daemon(8008, 2)
|
|
1507
|
+
|
|
1508
|
+
```
|
|
1509
|
+
|
|
1510
|
+
**注意,这需要你开启loadMonitor选项,这是默认开启的。**
|
|
1511
|
+
|
|
1512
|
+
---
|
|
1513
|
+
|
|
1514
|
+
# 🧩 中间件扩展和其他扩展 📜
|
|
1515
|
+
|
|
1516
|
+
---
|
|
1517
|
+
|
|
1518
|
+
作为Web框架,仅仅提供基础功能是不够的,很多基础功能在所有Web服务上都会用到,所以必然会出现通用的扩展模块。
|
|
1519
|
+
|
|
1520
|
+
在Topbit全局中间件设计机制上,大部分和Web服务有关的扩展都可以通过开发一个中间件扩展完成,可以参考 `中间件` 部分,这种插件机制十分灵活。
|
|
1521
|
+
|
|
1522
|
+
**Topbit提供了一些通用中间件扩展,开发者可以按需使用。**
|
|
1523
|
+
|
|
1524
|
+
**也提供了一些其他扩展模块:路由加载器、Token会话验证、命令参数解析、错误日志自动化处理、zip压缩和解压。**
|
|
1525
|
+
|
|
1526
|
+
## 导入中间件扩展
|
|
1527
|
+
|
|
1528
|
+
所有中间件扩展都在Topbit.extensions下,示例:
|
|
1529
|
+
|
|
1530
|
+
```javascript
|
|
1531
|
+
'use strict'
|
|
1532
|
+
|
|
1533
|
+
process.chdir(__dirname)
|
|
1534
|
+
|
|
1535
|
+
const Topbit = require('Topbit')
|
|
1536
|
+
|
|
1537
|
+
//通过解构赋值导入扩展
|
|
1538
|
+
const {Cors, ToFile} = Topbit.extensions
|
|
1539
|
+
|
|
1540
|
+
const app = new Topbit({
|
|
1541
|
+
debug: true,
|
|
1542
|
+
//开启日志,是globalLog的别名
|
|
1543
|
+
log: true
|
|
1544
|
+
})
|
|
1545
|
+
|
|
1546
|
+
// 跨域扩展在解析body之前运行
|
|
1547
|
+
// use添加的中间件解析body数据之后运行
|
|
1548
|
+
app.pre(new Cors())
|
|
1549
|
+
.use(new ToFile, {method: ['PUT', 'POST']})
|
|
1550
|
+
|
|
1551
|
+
app.post('/file', async ctx => {
|
|
1552
|
+
let f = ctx.getFile('file')
|
|
1553
|
+
if (!f) return ctx.status(400).to('file not found')
|
|
1554
|
+
|
|
1555
|
+
ctx.to(
|
|
1556
|
+
await f.toFile('./uploads')
|
|
1557
|
+
)
|
|
1558
|
+
})
|
|
1559
|
+
|
|
1560
|
+
app.run(1235)
|
|
1561
|
+
```
|
|
1562
|
+
|
|
1563
|
+
## ☲ 中间件扩展
|
|
1564
|
+
|
|
1565
|
+
|
|
1566
|
+
| 中间件 | 描述 |
|
|
1567
|
+
|----------------|----------------------------------------------------------------------|
|
|
1568
|
+
| Cors | 支持跨域请求的中间件。 |
|
|
1569
|
+
| Resource | 静态资源处理,是目前最强静态资源处理服务。 |
|
|
1570
|
+
| ToFile | 让上传的文件对象具备 `toFile` 方法。 |
|
|
1571
|
+
| SSE | SSE协议中间件,基于HTTP的数据流推送。 |
|
|
1572
|
+
| Proxy | 针对 HTTP/1.1 协议的反向代理,支持负载均衡和 alive 检测。 |
|
|
1573
|
+
| Http2Proxy | HTTP/2 协议的反向代理,支持负载均衡和 alive 检测。 |
|
|
1574
|
+
| SNI | HTTPS 多域名支持。 |
|
|
1575
|
+
| ParamCheck | 请求参数检测,支持 `param`、`query`、`body` 的检测。 |
|
|
1576
|
+
| JWT | JWT 协议的 token 验证,推荐使用 Topbit.Token,比 JWT 强太多。 |
|
|
1577
|
+
| Cookie | 解析前端请求的消息头 cookie。 |
|
|
1578
|
+
| Session | Session 会话,使用的是本地文件机制。 |
|
|
1579
|
+
|
|
1580
|
+
---
|
|
1581
|
+
|
|
1582
|
+
以下是根据你提供的 `titbit-toolkit` 原始文档内容,按照新的列表顺序重新整理的文档。
|
|
1583
|
+
|
|
1584
|
+
**注意**:
|
|
1585
|
+
* 根据指示,文档已针对新的 **Topbit** 框架语境进行了调整(即不再强调独立安装扩展)。
|
|
1586
|
+
* 列表中未包含的组件(如 `Timing`, `RealIP`, `MixLogger`, `SendType`, `SetFinal`, `Http2Limit`)已被移除。
|
|
1587
|
+
* 列表中包含但原文档未提供详细说明的组件(如 `SNI`),仅保留了描述占位符。
|
|
1588
|
+
|
|
1589
|
+
---
|
|
1590
|
+
|
|
1591
|
+
### 1. Cors (跨域支持)
|
|
1592
|
+
|
|
1593
|
+
支持跨域请求的中间件。尽管是对跨域场景的支持,但在具备 `origin` 消息头的跨域通信和 `referer` 资源引入场景都做了支持,并且可以灵活选择和配置。
|
|
1594
|
+
|
|
1595
|
+
**基本使用:**
|
|
1596
|
+
|
|
1597
|
+
```javascript
|
|
1598
|
+
const Topbit = require('topbit')
|
|
1599
|
+
const {Cors} = Topbit.extensions
|
|
1600
|
+
|
|
1601
|
+
let cr = new Cors({
|
|
1602
|
+
// 默认为 * 表示全部开启跨域,
|
|
1603
|
+
// 若要指定要支持的域名则需要传递一个数组。
|
|
1604
|
+
// 注意:这里指定的是用于访问接口的应用页面所在域。
|
|
1605
|
+
allow : [
|
|
1606
|
+
'https://a.com',
|
|
1607
|
+
'https://www.a.com',
|
|
1608
|
+
],
|
|
1609
|
+
|
|
1610
|
+
// 默认只有 content-type
|
|
1611
|
+
allowHeaders : 'authorization,content-type',
|
|
1612
|
+
|
|
1613
|
+
// OPTIONS 请求缓存 60 秒,此期间浏览器请求会先去读取缓存。
|
|
1614
|
+
optionsCache: 60,
|
|
1615
|
+
|
|
1616
|
+
// 默认为 '*',可以设置允许的请求方法。
|
|
1617
|
+
// requestHeaders : '*'
|
|
1618
|
+
|
|
1619
|
+
// 默认为 '',表示哪些消息头可以暴露给请求端,多个消息头使用 , 分隔。
|
|
1620
|
+
// exposeHeaders: 'x-test-key,x-type'
|
|
1621
|
+
})
|
|
1622
|
+
|
|
1623
|
+
app.pre(cr)
|
|
1624
|
+
|
|
1625
|
+
// 支持 OPTIONS 请求,浏览器在处理 POST/PUT/DELETE 时会先发送 OPTIONS 预检请求。
|
|
1626
|
+
// 必须处理 OPTIONS 请求才能完整支持跨域。
|
|
1627
|
+
app.options('/*', async c => {})
|
|
1628
|
+
```
|
|
1629
|
+
|
|
1630
|
+
**高级配置(Referer检测与分组):**
|
|
1631
|
+
|
|
1632
|
+
```javascript
|
|
1633
|
+
let cr = new cors({
|
|
1634
|
+
// 不允许 referer 为空
|
|
1635
|
+
allowEmptyReferer: false,
|
|
1636
|
+
|
|
1637
|
+
// 允许 referer 为空的路由分组,
|
|
1638
|
+
// 分组名称是自定义的,参考框架的路由分组功能。
|
|
1639
|
+
// 此功能主要用于在 API 开发中还要兼顾页面的服务上。
|
|
1640
|
+
emptyRefererGroup: [
|
|
1641
|
+
'@webapp', '@pages'
|
|
1642
|
+
],
|
|
1643
|
+
|
|
1644
|
+
allow: [
|
|
1645
|
+
// 默认的字符串形式表示 origin 跨域和 referer 外链接引用都被允许
|
|
1646
|
+
'https://w.a.com',
|
|
1647
|
+
'https://w.x.cn',
|
|
1648
|
+
|
|
1649
|
+
{url: 'https://servicewechat.com/wx234hiohr23', referer: true},
|
|
1650
|
+
// 不应用于 referer 检测,如果有请求提交了 referer 为 self-define 不会通过检测
|
|
1651
|
+
{url: 'self-define', referer: false}
|
|
1652
|
+
],
|
|
1653
|
+
})
|
|
1654
|
+
```
|
|
1655
|
+
|
|
1656
|
+
---
|
|
1657
|
+
|
|
1658
|
+
### 2. Resource (静态资源处理)
|
|
1659
|
+
|
|
1660
|
+
静态资源处理服务,主要用于 JS、CSS、图片、音频等静态文件的托管。支持缓存控制、路由前缀修正等功能。
|
|
1661
|
+
|
|
1662
|
+
**基本使用:**
|
|
1663
|
+
|
|
1664
|
+
```javascript
|
|
1665
|
+
const Topbit = require('topbit')
|
|
1666
|
+
const {Resource} = Topbit.extensions
|
|
1667
|
+
|
|
1668
|
+
let st = new Resource({
|
|
1669
|
+
// 设定静态资源所在目录
|
|
1670
|
+
staticPath: './public'
|
|
1671
|
+
})
|
|
1672
|
+
|
|
1673
|
+
// 只对分组为 static 执行中间件。
|
|
1674
|
+
app.use(st, {group: 'static'})
|
|
1675
|
+
|
|
1676
|
+
// 添加静态资源路由,假设 public 目录下有 css/a.css
|
|
1677
|
+
// 访问 /static/css/a.css 即可
|
|
1678
|
+
app.get('/static/*', async c => {
|
|
1679
|
+
// 请求分组为 static
|
|
1680
|
+
}, '@static')
|
|
1681
|
+
```
|
|
1682
|
+
|
|
1683
|
+
**快速示例与配置详解:**
|
|
1684
|
+
|
|
1685
|
+
```javascript
|
|
1686
|
+
let st = new Resource({
|
|
1687
|
+
// 设定静态资源所在目录
|
|
1688
|
+
staticPath: './public',
|
|
1689
|
+
|
|
1690
|
+
// 路由路径:前端必须以 /static/ 开头,映射到 ./public 目录下的文件
|
|
1691
|
+
routePath : '/static/*',
|
|
1692
|
+
|
|
1693
|
+
// 指定路由分组
|
|
1694
|
+
routeGroup: '_static',
|
|
1695
|
+
|
|
1696
|
+
// 是否支持中文路径解码(默认 false)
|
|
1697
|
+
decodePath: true,
|
|
1698
|
+
|
|
1699
|
+
// 前缀路径修正。如果设置为 xyz,会自动修正为 /xyz。
|
|
1700
|
+
// prepath : '',
|
|
1701
|
+
|
|
1702
|
+
// 最大缓存文件大小,超过此大小则不会缓存(单位字节)
|
|
1703
|
+
maxFileSize: 12_000_000,
|
|
1704
|
+
|
|
1705
|
+
// 设置消息头 cache-control 的值,默认为 null
|
|
1706
|
+
cacheControl: 'max-age=3600',
|
|
1707
|
+
|
|
1708
|
+
// 最大总缓存大小(默认 120MB)
|
|
1709
|
+
maxCacheSize: 120_000_000,
|
|
1710
|
+
|
|
1711
|
+
// 失败限制次数与缓存释放概率
|
|
1712
|
+
failedLimit: 50,
|
|
1713
|
+
prob: 6
|
|
1714
|
+
})
|
|
1715
|
+
|
|
1716
|
+
st.init(app)
|
|
1717
|
+
|
|
1718
|
+
// 自动添加到 _static 分组。
|
|
1719
|
+
// 例如:public 目录中有 favicon.ico,通过此请求即可获取。
|
|
1720
|
+
app.get('/favicon.ico', async c => {}, {group: '_static'})
|
|
1721
|
+
```
|
|
1722
|
+
|
|
1723
|
+
**接口说明:**
|
|
1724
|
+
* `addType(obj)`: 添加扩展名到 content-type 的映射关系。
|
|
1725
|
+
* `clearCache()`: 清理缓存。
|
|
1726
|
+
|
|
1727
|
+
---
|
|
1728
|
+
|
|
1729
|
+
### 3. ToFile (文件上传对象化)
|
|
1730
|
+
|
|
1731
|
+
让上传的文件对象具备 `toFile` 方法,便于按照面向对象的风格保存文件。
|
|
1732
|
+
|
|
1733
|
+
```javascript
|
|
1734
|
+
const Topbit = require('topbit')
|
|
1735
|
+
const {ToFile} = Topbit.extensions
|
|
1736
|
+
|
|
1737
|
+
app.use( new ToFile() )
|
|
1738
|
+
|
|
1739
|
+
app.post('/upload', async c => {
|
|
1740
|
+
// 获取上传的文件对象
|
|
1741
|
+
let f = c.getFile('image')
|
|
1742
|
+
|
|
1743
|
+
if (!f) {
|
|
1744
|
+
return c.status(400).oo('image file not found')
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
// 把文件移动到 images 目录(需确保目录存在)。
|
|
1748
|
+
// 第二个参数可选指定文件名,默认根据时间戳和随机数生成唯一文件名。
|
|
1749
|
+
let fname = await f.toFile('./images')
|
|
1750
|
+
|
|
1751
|
+
// 返回文件名
|
|
1752
|
+
c.ok(fname)
|
|
1753
|
+
})
|
|
1754
|
+
|
|
1755
|
+
app.run(1234)
|
|
1756
|
+
|
|
1757
|
+
```
|
|
1758
|
+
|
|
1759
|
+
---
|
|
1760
|
+
|
|
1761
|
+
### 4. SSE (服务端消息推送)
|
|
1762
|
+
|
|
1763
|
+
基于 HTTP 协议的 Server Sent Events (SSE) 中间件,用于实现服务端主动向客户端推送数据流。
|
|
1764
|
+
|
|
1765
|
+
**基本使用:**
|
|
1766
|
+
|
|
1767
|
+
```javascript
|
|
1768
|
+
const Topbit = require('topbit')
|
|
1769
|
+
const {SSE} = Topbit.extensions
|
|
1770
|
+
|
|
1771
|
+
let s = new SSE({
|
|
1772
|
+
// 超时20秒,超过此时间后会自动关闭连接
|
|
1773
|
+
timeout: 20000,
|
|
1774
|
+
|
|
1775
|
+
// 重新发起连接时间,默认值为0表示不再发起连接
|
|
1776
|
+
retry: 5000,
|
|
1777
|
+
|
|
1778
|
+
// 定时器间隔,每隔2秒执行一次处理函数
|
|
1779
|
+
timeSlice: 2000
|
|
1780
|
+
})
|
|
1781
|
+
|
|
1782
|
+
// 设置处理函数,ctx 为请求上下文
|
|
1783
|
+
s.handle = (ctx) => {
|
|
1784
|
+
// 发送单条消息
|
|
1785
|
+
ctx.sendmsg({event: 'eat', data: '饭'})
|
|
1786
|
+
|
|
1787
|
+
if (Date.now() % 5 === 0) {
|
|
1788
|
+
// 发送多条消息,字符串或数字会自动转换为对应的消息格式,事件默认为 message
|
|
1789
|
+
ctx.sendmsg([
|
|
1790
|
+
{event: 'clock', data: Date.now()},
|
|
1791
|
+
'但愿人长久,千里共婵娟。',
|
|
1792
|
+
Math.random()
|
|
1793
|
+
])
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
// 只针对路由分组 sse 启用中间件
|
|
1798
|
+
app.use( s, {group: 'sse'} )
|
|
1799
|
+
|
|
1800
|
+
app.get('/sse', async ctx => {}, {group: 'sse'})
|
|
1801
|
+
|
|
1802
|
+
app.run(1234)
|
|
1803
|
+
|
|
1804
|
+
```
|
|
1805
|
+
|
|
1806
|
+
**生成器模式 (Generator Mode):**
|
|
1807
|
+
适用于需要动态调整时间间隔或更精确控制的场景。
|
|
1808
|
+
|
|
1809
|
+
```javascript
|
|
1810
|
+
let s = new SSE({
|
|
1811
|
+
timeout: 20000,
|
|
1812
|
+
retry: 5000,
|
|
1813
|
+
timeSlice: 10,
|
|
1814
|
+
mode: 'yield' // 开启生成器模式
|
|
1815
|
+
})
|
|
1816
|
+
|
|
1817
|
+
s.handle = (ctx) => {
|
|
1818
|
+
ctx.sendmsg({event: 'eat', data: '饭'})
|
|
1819
|
+
// 使用 await ctx.sse.moment(ms) 进行延迟
|
|
1820
|
+
await ctx.sse.moment(100)
|
|
1821
|
+
}
|
|
1822
|
+
```
|
|
1823
|
+
|
|
1824
|
+
---
|
|
1825
|
+
|
|
1826
|
+
### 5. Proxy (HTTP/1.1 反向代理)
|
|
1827
|
+
|
|
1828
|
+
针对 HTTP/1.1 协议的反向代理,支持负载均衡和存活检测 (Alive Check)。
|
|
1829
|
+
|
|
1830
|
+
**基本代理配置:**
|
|
1831
|
+
|
|
1832
|
+
```javascript
|
|
1833
|
+
const Topbit = require('topbit')
|
|
1834
|
+
const {Proxy} = Topbit.extensions
|
|
1835
|
+
|
|
1836
|
+
let hostcfg = {
|
|
1837
|
+
// 简单映射:默认 path 为 /
|
|
1838
|
+
'a.com' : 'http://localhost:8001',
|
|
1839
|
+
|
|
1840
|
+
// 指定路径映射
|
|
1841
|
+
'b.com' : {
|
|
1842
|
+
path : '/xyz',
|
|
1843
|
+
url : 'http://localhost:8002'
|
|
1844
|
+
},
|
|
1845
|
+
|
|
1846
|
+
// 复杂配置与重写
|
|
1847
|
+
'c.com' : [
|
|
1848
|
+
{
|
|
1849
|
+
path : '/',
|
|
1850
|
+
url : 'http://localhost:8004',
|
|
1851
|
+
rewrite: (ctx, path) => {
|
|
1852
|
+
// 自定义重写路由规则
|
|
1853
|
+
if (path.indexOf('/xyz') === 0) {
|
|
1854
|
+
return path.replace('/xyz', '/w')
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
]
|
|
1859
|
+
};
|
|
1860
|
+
|
|
1861
|
+
const pxy = new Proxy({
|
|
1862
|
+
timeout: 10000,
|
|
1863
|
+
config: hostcfg
|
|
1864
|
+
})
|
|
1865
|
+
|
|
1866
|
+
pxy.init(app)
|
|
1867
|
+
|
|
1868
|
+
app.run(1234)
|
|
1869
|
+
|
|
1870
|
+
```
|
|
1871
|
+
|
|
1872
|
+
**负载均衡配置:**
|
|
1873
|
+
通过配置数组实现,支持权重 (`weight`) 和存活检测。
|
|
1874
|
+
|
|
1875
|
+
```javascript
|
|
1876
|
+
let load_balance_cfg = {
|
|
1877
|
+
'a.com' : [
|
|
1878
|
+
{
|
|
1879
|
+
path : '/',
|
|
1880
|
+
url : 'http://localhost:1234',
|
|
1881
|
+
// 每3秒检测服务存活
|
|
1882
|
+
aliveCheckInterval : 3,
|
|
1883
|
+
aliveCheckPath : '/alive-check',
|
|
1884
|
+
// 权重,数字越大权重越高
|
|
1885
|
+
weight: 3
|
|
1886
|
+
},
|
|
1887
|
+
{
|
|
1888
|
+
path : '/',
|
|
1889
|
+
url : 'http://localhost:1235',
|
|
1890
|
+
aliveCheckInterval : 3,
|
|
1891
|
+
aliveCheckPath : '/ok',
|
|
1892
|
+
weight: 2
|
|
1893
|
+
}
|
|
1894
|
+
]
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
const pxy = new Proxy({
|
|
1898
|
+
timeout: 10000,
|
|
1899
|
+
starPath : true, // 开启后将代理路径后的字符串作为路径转发
|
|
1900
|
+
config: load_balance_cfg
|
|
1901
|
+
})
|
|
1902
|
+
|
|
1903
|
+
pxy.init(app)
|
|
1904
|
+
```
|
|
1905
|
+
|
|
1906
|
+
---
|
|
1907
|
+
|
|
1908
|
+
### 6. Http2Proxy (HTTP/2 反向代理)
|
|
1909
|
+
|
|
1910
|
+
HTTP/2 协议的反向代理,参数与 `Proxy` 基本一致,利用 HTTP/2 连接机制进行保活。
|
|
1911
|
+
|
|
1912
|
+
```javascript
|
|
1913
|
+
const Topbit = require('topbit')
|
|
1914
|
+
const {Http2Proxy} = Topbit.extensions
|
|
1915
|
+
|
|
1916
|
+
let hxy = new Http2Proxy({
|
|
1917
|
+
config: {
|
|
1918
|
+
'a.com' : [
|
|
1919
|
+
{
|
|
1920
|
+
url: 'http://localhost:3001',
|
|
1921
|
+
weight: 10,
|
|
1922
|
+
path : '/',
|
|
1923
|
+
reconnDelay: 200, // 重连延迟
|
|
1924
|
+
max: 2,
|
|
1925
|
+
headers: {
|
|
1926
|
+
'x-test-key': `${Date.now()}`
|
|
1927
|
+
},
|
|
1928
|
+
connectTimeout: 2000
|
|
1929
|
+
},
|
|
1930
|
+
{
|
|
1931
|
+
url: 'http://localhost:3002',
|
|
1932
|
+
weight: 4,
|
|
1933
|
+
path : '/'
|
|
1934
|
+
}
|
|
1935
|
+
]
|
|
1936
|
+
},
|
|
1937
|
+
debug: true
|
|
1509
1938
|
})
|
|
1510
1939
|
|
|
1511
|
-
|
|
1940
|
+
hxy.init(app)
|
|
1512
1941
|
|
|
1942
|
+
app.run(1234)
|
|
1513
1943
|
```
|
|
1514
1944
|
|
|
1515
|
-
|
|
1945
|
+
---
|
|
1946
|
+
|
|
1947
|
+
### 7. SNI (HTTPS 多域名支持)
|
|
1948
|
+
|
|
1949
|
+
**描述**:用于在同一 IP 地址和端口上支持多个 HTTPS 域名证书的中间件。
|
|
1950
|
+
|
|
1951
|
+
感谢提供源代码。根据代码逻辑,`SNI` 扩展通过 `init` 方法将 `SNICallback` 注入到应用的 `config.server` 配置中,从而利用 Node.js 原生 TLS 的能力实现多域名证书支持。
|
|
1952
|
+
|
|
1953
|
+
以下是补全后的 **SNI** 文档部分:
|
|
1954
|
+
|
|
1955
|
+
---
|
|
1956
|
+
|
|
1957
|
+
### 7. SNI (HTTPS 多域名支持)
|
|
1958
|
+
|
|
1959
|
+
**描述**:用于在同一 IP 地址和端口上支持多个 HTTPS 域名证书的中间件。它利用 TLS 协议的 Server Name Indication 特性,根据客户端请求的域名动态加载对应的 SSL 证书。
|
|
1960
|
+
|
|
1961
|
+
**注意**:初始化时会同步读取证书文件,请确保路径正确。如果某个域名的证书读取失败,会在控制台输出错误信息,但不会阻塞其他域名的加载。
|
|
1962
|
+
|
|
1963
|
+
**使用:**
|
|
1964
|
+
|
|
1965
|
+
```javascript
|
|
1966
|
+
const Topbit = require('topbit')
|
|
1967
|
+
const {SNI} = Topbit.extensions
|
|
1968
|
+
|
|
1969
|
+
const app = new Topbit({
|
|
1970
|
+
// 启用 HTTPS,通常建议配置一组默认的 key 和 cert 作为回退
|
|
1971
|
+
https: true,
|
|
1972
|
+
key: './ssl/default.key',
|
|
1973
|
+
cert: './ssl/default.cert',
|
|
1974
|
+
debug: true
|
|
1975
|
+
})
|
|
1976
|
+
|
|
1977
|
+
// 配置多域名证书
|
|
1978
|
+
// Key 为域名,Value 为包含 key 和 cert 路径的对象
|
|
1979
|
+
let certs = {
|
|
1980
|
+
'a.com': {
|
|
1981
|
+
key: './ssl/a.com.key',
|
|
1982
|
+
cert: './ssl/a.com.cert'
|
|
1983
|
+
},
|
|
1984
|
+
'www.b.org': {
|
|
1985
|
+
key: '/etc/nginx/ssl/b.org.key',
|
|
1986
|
+
cert: '/etc/nginx/ssl/b.org.crt'
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
// 实例化并初始化
|
|
1991
|
+
let sni = new SNI(certs)
|
|
1992
|
+
|
|
1993
|
+
// init 方法会将 SNICallback 注入到 app.config.server 中
|
|
1994
|
+
sni.init(app)
|
|
1995
|
+
|
|
1996
|
+
app.run(443)
|
|
1997
|
+
```
|
|
1998
|
+
|
|
1999
|
+
**参数说明**:
|
|
2000
|
+
* 构造函数接受一个对象,键名为**域名**,键值为配置对象。
|
|
2001
|
+
* 配置对象必须包含:
|
|
2002
|
+
* `key`: 私钥文件的路径。
|
|
2003
|
+
* `cert`: 证书文件的路径。
|
|
2004
|
+
|
|
2005
|
+
---
|
|
2006
|
+
|
|
2007
|
+
### 8. ParamCheck (请求参数检测)
|
|
2008
|
+
|
|
2009
|
+
支持对 `param` (路由参数)、`query` (查询字符串)、`body` (请求体) 进行声明式检测。
|
|
2010
|
+
|
|
2011
|
+
**Query 和 Param 检测:**
|
|
2012
|
+
|
|
2013
|
+
```javascript
|
|
2014
|
+
const Topbit = require('topbit')
|
|
2015
|
+
const {ParamCheck} = Topbit.extensions
|
|
2016
|
+
|
|
2017
|
+
// Query 参数检测
|
|
2018
|
+
let pck = new ParamCheck({
|
|
2019
|
+
key: 'query',
|
|
2020
|
+
data : {
|
|
2021
|
+
// 严格限制值
|
|
2022
|
+
say: 'hello',
|
|
2023
|
+
// 类型转换与范围限制
|
|
2024
|
+
offset: {
|
|
2025
|
+
default: 0,
|
|
2026
|
+
to: 'int',
|
|
2027
|
+
min: 0,
|
|
2028
|
+
max: 100
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
})
|
|
2032
|
+
|
|
2033
|
+
// Param 参数检测与自定义回调
|
|
2034
|
+
let paramck = new ParamCheck({
|
|
2035
|
+
key: 'param',
|
|
2036
|
+
deny: ['x-key'], // 禁止提交的字段
|
|
2037
|
+
deleteDeny: true,
|
|
2038
|
+
data : {
|
|
2039
|
+
errorMessage: '参数错误', // 自定义错误信息
|
|
2040
|
+
mobile: {
|
|
2041
|
+
callback: (obj, k, method) => {
|
|
2042
|
+
let preg = /^(12|13|15|16|17|18|19)[0-9]{9}$/
|
|
2043
|
+
return preg.test(obj[k])
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
})
|
|
2048
|
+
|
|
2049
|
+
app.use(pck, {method: 'GET'})
|
|
2050
|
+
.use(paramck, {method: 'GET'})
|
|
2051
|
+
```
|
|
2052
|
+
|
|
2053
|
+
**Body 检测:**
|
|
2054
|
+
|
|
2055
|
+
```javascript
|
|
2056
|
+
let pmbody = new ParamCheck({
|
|
2057
|
+
key: 'body',
|
|
2058
|
+
data: {
|
|
2059
|
+
username: { must: true },
|
|
2060
|
+
passwd: { must: true }
|
|
2061
|
+
}
|
|
2062
|
+
})
|
|
2063
|
+
|
|
2064
|
+
app.use(pmbody, {method: ['POST', 'PUT'], name: 'login'})
|
|
2065
|
+
```
|
|
2066
|
+
|
|
2067
|
+
---
|
|
2068
|
+
|
|
2069
|
+
### 9. JWT (Token 验证)
|
|
2070
|
+
|
|
2071
|
+
JWT 协议的 Token 验证与签发。推荐使用 `Topbit.Token` 替代。
|
|
2072
|
+
|
|
2073
|
+
```javascript
|
|
2074
|
+
const Topbit = require('topbit')
|
|
2075
|
+
const {JWT} = Topbit.extensions
|
|
2076
|
+
|
|
2077
|
+
let j = new JWT({
|
|
2078
|
+
timeout: 7200000, // 超时时间
|
|
2079
|
+
})
|
|
2080
|
+
j.alg = 'hs512' // 设置算法
|
|
2081
|
+
|
|
2082
|
+
// 签发 Token
|
|
2083
|
+
let token = j.make({
|
|
2084
|
+
id: '123we',
|
|
2085
|
+
username: 'long'
|
|
2086
|
+
})
|
|
2087
|
+
|
|
2088
|
+
// 验证 Token
|
|
2089
|
+
// 返回对象:{ ok: true, data: {...} } 或 { ok: false, errcode: '...' }
|
|
2090
|
+
let r = j.verify(token)
|
|
2091
|
+
|
|
2092
|
+
// 作为中间件使用
|
|
2093
|
+
// 验证成功会将数据挂载到 ctx.box.user
|
|
2094
|
+
app.pre(j.mid())
|
|
2095
|
+
```
|
|
2096
|
+
|
|
2097
|
+
---
|
|
2098
|
+
|
|
2099
|
+
### 10. Cookie (Cookie 解析)
|
|
2100
|
+
|
|
2101
|
+
解析前端请求消息头中的 Cookie,并在请求上下文中添加 `cookie` 属性。
|
|
2102
|
+
|
|
2103
|
+
```javascript
|
|
2104
|
+
const Topbit = require('topbit')
|
|
2105
|
+
const {Cookie} = Topbit.extensions
|
|
2106
|
+
|
|
2107
|
+
app.use( new Cookie() )
|
|
2108
|
+
|
|
2109
|
+
app.get('/', async ctx => {
|
|
2110
|
+
// 获取解析后的 cookie 对象
|
|
2111
|
+
console.log(ctx.cookie)
|
|
2112
|
+
})
|
|
2113
|
+
|
|
2114
|
+
app.run(1234)
|
|
2115
|
+
|
|
2116
|
+
```
|
|
2117
|
+
|
|
2118
|
+
---
|
|
2119
|
+
|
|
2120
|
+
### 11. Session (会话管理)
|
|
2121
|
+
|
|
2122
|
+
基于本地文件机制的 Session 会话管理,依赖 Cookie 组件。
|
|
2123
|
+
**注意**:此扩展主要用于测试和教学,生产环境建议使用 Redis 或其他数据库存储方案。
|
|
2124
|
+
|
|
2125
|
+
```javascript
|
|
2126
|
+
const Topbit = require('topbit')
|
|
2127
|
+
const {Cookie,Session} = Topbit.extensions
|
|
2128
|
+
|
|
2129
|
+
let sess = new Session()
|
|
2130
|
+
sess.init(app) // 初始化:添加 ctx 原型方法
|
|
2131
|
+
|
|
2132
|
+
// 必须先启用 Cookie,再启用 Session
|
|
2133
|
+
app.use( new Cookie() ).use( sess )
|
|
2134
|
+
|
|
2135
|
+
app.get('/:key/:data', async ctx => {
|
|
2136
|
+
// 设置 Session
|
|
2137
|
+
ctx.setSession(ctx.param.key, ctx.param.data)
|
|
2138
|
+
|
|
2139
|
+
// 获取 Session (key 默认为 null 获取所有)
|
|
2140
|
+
let data = ctx.getSession()
|
|
2141
|
+
|
|
2142
|
+
ctx.ok(data)
|
|
2143
|
+
})
|
|
2144
|
+
|
|
2145
|
+
app.run(1234)
|
|
2146
|
+
|
|
2147
|
+
// 其他方法:
|
|
2148
|
+
// ctx.delSession(key) - 删除指定 Session
|
|
2149
|
+
// ctx.clearSession() - 清空 Session
|
|
2150
|
+
```
|
|
2151
|
+
|
|
2152
|
+
---
|
|
2153
|
+
|
|
2154
|
+
## ⌨️命令参数解析
|
|
2155
|
+
|
|
2156
|
+
**用于快速解析process.argv。**
|
|
2157
|
+
|
|
2158
|
+
|
|
2159
|
+
### 使用示例
|
|
2160
|
+
|
|
2161
|
+
```javascript
|
|
2162
|
+
|
|
2163
|
+
'use strict'
|
|
2164
|
+
|
|
2165
|
+
const Topbit = require('Topbit')
|
|
2166
|
+
const parseArgv = Topbit.npargv
|
|
2167
|
+
|
|
2168
|
+
let ret = parseArgv({
|
|
2169
|
+
'--port=' : {
|
|
2170
|
+
name: 'port',
|
|
2171
|
+
//别名
|
|
2172
|
+
alias: '-p',
|
|
2173
|
+
type: 'int',
|
|
2174
|
+
min: 2000,
|
|
2175
|
+
max: 2100
|
|
2176
|
+
},
|
|
2177
|
+
|
|
2178
|
+
'--host=' : {
|
|
2179
|
+
name: 'host',
|
|
2180
|
+
match: /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/i
|
|
2181
|
+
},
|
|
2182
|
+
|
|
2183
|
+
'-w' : {
|
|
2184
|
+
name: 'worker',
|
|
2185
|
+
type: 'int',
|
|
2186
|
+
min: 1,
|
|
2187
|
+
max: 4,
|
|
2188
|
+
//若设置了默认值,则在参数不合法时会自动使用默认值不会返回错误信息。
|
|
2189
|
+
default: 2
|
|
2190
|
+
},
|
|
2191
|
+
|
|
2192
|
+
'--https': {
|
|
2193
|
+
name: 'https'
|
|
2194
|
+
},
|
|
2195
|
+
|
|
2196
|
+
'--http2': {
|
|
2197
|
+
name: 'http2'
|
|
2198
|
+
},
|
|
2199
|
+
|
|
2200
|
+
'--test': {
|
|
2201
|
+
name: 'test'
|
|
2202
|
+
},
|
|
2203
|
+
|
|
2204
|
+
'--id' : {
|
|
2205
|
+
name : 'id',
|
|
2206
|
+
callback: (str) => {
|
|
2207
|
+
return str.split(',').filter(p => p.length > 0)
|
|
2208
|
+
}
|
|
2209
|
+
},
|
|
2210
|
+
|
|
2211
|
+
//自动转换为{type: 'bool', name: 'limit'}
|
|
2212
|
+
'--limit' : 'limit',
|
|
2213
|
+
|
|
2214
|
+
//自动转换为{type: 'bool', name: '-x'}
|
|
2215
|
+
'-x' : false
|
|
2216
|
+
|
|
2217
|
+
})
|
|
2218
|
+
|
|
2219
|
+
console.log(ret)
|
|
2220
|
+
|
|
2221
|
+
```
|
|
2222
|
+
|
|
2223
|
+
运行命令参数:
|
|
2224
|
+
|
|
2225
|
+
```shell
|
|
2226
|
+
$ node test.js x --host=1.2.3.4 --https --http2 --test --port=2010 --id 1,2,3,4,5 --local a b c
|
|
2227
|
+
```
|
|
2228
|
+
|
|
2229
|
+
运行输出结果:
|
|
2230
|
+
|
|
2231
|
+
```javascript
|
|
2232
|
+
{
|
|
2233
|
+
ok: true,
|
|
2234
|
+
errmsg: '',
|
|
2235
|
+
args: {
|
|
2236
|
+
worker: 2,
|
|
2237
|
+
https: true,
|
|
2238
|
+
http2: true,
|
|
2239
|
+
test: true,
|
|
2240
|
+
limit: false,
|
|
2241
|
+
host: '1.2.3.4',
|
|
2242
|
+
port: 2010,
|
|
2243
|
+
id: [ '1', '2', '3', '4', '5' ],
|
|
2244
|
+
'--local': true,
|
|
2245
|
+
'-x': false
|
|
2246
|
+
},
|
|
2247
|
+
// 不是参数,也不是-开头的会放在list中,方便获取。
|
|
2248
|
+
// 如果存在位置引用的参数,则会优先解析,所以x没有在list中。
|
|
2249
|
+
list: ['a', 'b', 'c']
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
```
|
|
2253
|
+
|
|
2254
|
+
解析后的结果,若ok为false,则errmsg会给出错误提示信息。若参数无错误,则args是解析后的参数对象。
|
|
2255
|
+
|
|
2256
|
+
解析过程中,若参数不在传递的对象描述信息内,仍然会在args中体现,key值即为参数的名字,可以参考示例中的--local。
|
|
2257
|
+
|
|
2258
|
+
如果对参数的描述信息不是object类型,则会转换为boolean类型,此时name值即为参数的名字。参考-x参数。
|
|
2259
|
+
|
|
2260
|
+
**若没有给定type限定范围,则会根据描述自动设定type,这可能会有错误判断,所以尽可能给定type限定。**
|
|
2261
|
+
|
|
2262
|
+
**type推断规则如下:**
|
|
2263
|
+
|
|
2264
|
+
- 若参数含有=,比如--port=,则type为string类型。
|
|
2265
|
+
|
|
2266
|
+
- 否则,若描述对象具备match或callback,则为string类型。
|
|
2267
|
+
|
|
2268
|
+
- 否则,若描述对象含有min或max则为int类型。
|
|
2269
|
+
|
|
2270
|
+
- 否则,若描述对象具备default,则如果default是数字或字符串类型,type和default类型一致。
|
|
2271
|
+
|
|
2272
|
+
- 否则,type为bool。
|
|
2273
|
+
|
|
2274
|
+
**type支持类型如下:**
|
|
2275
|
+
|
|
2276
|
+
- int 或 number,整数类型。
|
|
2277
|
+
|
|
2278
|
+
- float,浮点数类型。
|
|
2279
|
+
|
|
2280
|
+
- string,字符串类型。
|
|
2281
|
+
|
|
2282
|
+
- bool或boolean,布尔类型。
|
|
2283
|
+
|
|
2284
|
+
### 参数描述的选项
|
|
2285
|
+
|
|
2286
|
+
| 选项 | 是否必须 | 描述 |
|
|
2287
|
+
| ---- | ---- | ---- |
|
|
2288
|
+
| type | 否 | 参数类型,若不给定则会根据描述信息自动设定。 |
|
|
2289
|
+
| name | 否 | 解析后参数的名字,就是解析后参数对象的key值。若不给定则使用参数名称。 |
|
|
2290
|
+
| min | 否 | 限制最小值。 |
|
|
2291
|
+
| max | 否 | 限制最大值。 |
|
|
2292
|
+
| default | 否 | 默认值,若设置,则检测到传递的参数不合法,会采用默认值不会返回错误信息。对于bool来说,default无效。默认值为false。 |
|
|
2293
|
+
| match | 否 | 正则表达式,若给定,会进行正则匹配。 |
|
|
2294
|
+
| callback | 否 | 函数,若给定,会把参数值传递到函数,若函数返回值不是undefined,则作为最后解析的值。 |
|
|
2295
|
+
|
|
2296
|
+
### autoDefault自动设定默认值
|
|
2297
|
+
|
|
2298
|
+
在程序解析过程中,有时候需要必须返回参数,不合法则使用默认值,这需要对每个选项都使用default属性,或者使用\@autoDefault让npargv自动设定默认值。
|
|
2299
|
+
|
|
2300
|
+
```javascript
|
|
2301
|
+
'use strict'
|
|
2302
|
+
|
|
2303
|
+
const Topbit = require('Topbit')
|
|
2304
|
+
const parseArgv = Topbit.npargv
|
|
2305
|
+
|
|
2306
|
+
let opts = {
|
|
2307
|
+
//启用自动设定默认值。
|
|
2308
|
+
'@autoDefault' : true,
|
|
2309
|
+
|
|
2310
|
+
//没有设定默认值,会自动设定默认值为min的值。
|
|
2311
|
+
'--port' : {
|
|
2312
|
+
min: 1234,
|
|
2313
|
+
max: 5678
|
|
2314
|
+
},
|
|
2315
|
+
|
|
2316
|
+
//没有default设定默认值,类型为数字也没有min的限制,会自动设置为0
|
|
2317
|
+
'-x' : {
|
|
2318
|
+
type : 'int'
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2321
|
+
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2324
|
+
let {args} = parseArgv(opts)
|
|
2325
|
+
|
|
2326
|
+
```
|
|
2327
|
+
|
|
2328
|
+
自动设定默认值的规则:
|
|
2329
|
+
|
|
2330
|
+
- 若type为bool则默认值为false
|
|
2331
|
+
|
|
2332
|
+
- 若type为string,则默认值为 空字符串。
|
|
2333
|
+
|
|
2334
|
+
- 若type为number、int、float,则默认值为min属性给定的值,若没有min则默认值为0。
|
|
2335
|
+
|
|
2336
|
+
### 第一个参数作为子命令
|
|
2337
|
+
|
|
2338
|
+
```javascript
|
|
2339
|
+
|
|
2340
|
+
let argscfg = {
|
|
2341
|
+
//定义支持的子命令
|
|
2342
|
+
'@command' : [
|
|
2343
|
+
'create', 'show', 'update', 'delete'
|
|
2344
|
+
],
|
|
2345
|
+
|
|
2346
|
+
//当不输入时,默认的命令
|
|
2347
|
+
'@defaultCommand': 'show'
|
|
2348
|
+
}
|
|
2349
|
+
|
|
2350
|
+
|
|
2351
|
+
```
|
|
2352
|
+
|
|
2353
|
+
### 位置引用参数
|
|
2354
|
+
|
|
2355
|
+
以 $ 加数字作为key值即表示这是一个位置相关的参数,在解析过程中会先进行位置引用参数的解析。
|
|
2356
|
+
|
|
2357
|
+
但是位置的索引数字和实际内部的索引有差异:
|
|
2358
|
+
|
|
2359
|
+
- 索引位置从1开始。
|
|
2360
|
+
|
|
2361
|
+
- 索引位置是参数值,不会包括命令名称。
|
|
2362
|
+
|
|
2363
|
+
- 如果设定了@command,则位置1会自动在解析时从command后开始。
|
|
2364
|
+
|
|
2365
|
+
注意:@command表示的是第一个参数,用于脚本的子命令功能。
|
|
2366
|
+
|
|
2367
|
+
```javascript
|
|
2368
|
+
const Topbit = require('Topbit')
|
|
2369
|
+
const npargv = Topbit.npargv
|
|
2370
|
+
|
|
2371
|
+
let {args} = npargv({
|
|
2372
|
+
'@autoDefault': true,
|
|
2373
|
+
'$1': {
|
|
2374
|
+
type: 'string',
|
|
2375
|
+
},
|
|
2376
|
+
'$2': {
|
|
2377
|
+
type: 'string',
|
|
2378
|
+
callback: (v) => {
|
|
2379
|
+
return ['i', 'o', 'v'].indexOf(v) >= 0 ? v : 'o'
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
})
|
|
2383
|
+
|
|
2384
|
+
```
|
|
2385
|
+
|
|
2386
|
+
---
|
|
2387
|
+
|
|
2388
|
+
## 🤖Loader(服务加载器)
|
|
2389
|
+
|
|
2390
|
+
TopbitLoader 是 Topbit 框架官方推荐的「自动化加载器」扩展,彻底告别手动 `app.get()`、`app.use()` 的繁琐写法。
|
|
2391
|
+
|
|
2392
|
+
它实现了真正的 **MCM 模式**(Middleware → Controller → Model),类似 MVC 但更轻量、更符合 Topbit 的极致性能哲学。
|
|
2393
|
+
|
|
2394
|
+
[🌐查看详细文档☛](./docs/topbit-loader.md)
|
|
2395
|
+
|
|
2396
|
+
---
|
|
2397
|
+
|
|
2398
|
+
## 🔐Token(会话验证)🪙
|
|
2399
|
+
|
|
2400
|
+
TopbitToken 是专为 Topbit 框架打造的零依赖、极简、高安全的加密用户凭证(Token)系统。
|
|
2401
|
+
|
|
2402
|
+
它完全基于 Node.js 原生 `crypto` 实现,支持:
|
|
2403
|
+
|
|
2404
|
+
- AES-256-GCM(默认,推荐)
|
|
2405
|
+
- AES-192-GCM / AES-128-CBC / AES-256-CBC
|
|
2406
|
+
- 国密 SM4-CBC
|
|
2407
|
+
|
|
2408
|
+
[🌐查看详细文档☛](./docs/topbit-token.md)
|
|
1516
2409
|
|
|
1517
|
-
|
|
2410
|
+
---
|