jj.js 0.8.3 → 0.8.6
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 +77 -20
- package/lib/db.js +12 -12
- package/lib/router.js +1 -1
- package/lib/run.js +17 -13
- package/lib/url.js +2 -1
- package/lib/utils/error.js +13 -12
- package/lib/view.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ A simple and lightweight MVC framework built on nodejs+koa2(一个基于nodejs
|
|
|
30
30
|
npm i jj.js
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
> 运行环境要求:node.js >= v12
|
|
33
|
+
> 运行环境要求:node.js >= v12 mysql >= 5.3
|
|
34
34
|
|
|
35
35
|
## Hello world !
|
|
36
36
|
|
|
@@ -105,7 +105,6 @@ node server.js
|
|
|
105
105
|
- **public**:静态资源目录,主要存放css文件、js文件、图片等静态资源。在`./config/app.js`中通过`static_dir`参数可以设置或更改静态目录名字,为空时,则关闭静态访问(系统不会加载静态资源访问的逻辑,最大节省内容,也即所谓的轻量)
|
|
106
106
|
- **server.js**:应用入口文件,名字可以任意改。
|
|
107
107
|
|
|
108
|
-
>
|
|
109
108
|
### 系统类库
|
|
110
109
|
|
|
111
110
|
```javascript
|
|
@@ -276,6 +275,16 @@ module.exports = Index;
|
|
|
276
275
|
|
|
277
276
|
> 注意:所有的输出和跳转并不会阻止后续代码执行,所以要终止代码执行,需在前面加上`return`。
|
|
278
277
|
|
|
278
|
+
> 方法:**async _init()** 控制器初始化
|
|
279
|
+
|
|
280
|
+
异步方法,除了使用控制器中间件执行一些初始化或验证外,控制器提供了这个更简单的方法。一旦控制器定义了这个方法,在通过url访问控制器其他公开方法时,在执行完控制器中间件后,都会先执行这个函数,如果想终止后续代码执行,可以在方法里返回`false`。
|
|
281
|
+
|
|
282
|
+
> 方法:**async _end()** 控制器结束
|
|
283
|
+
|
|
284
|
+
如果定义了这个函数,在通过url访问控制器其他公开方法后,会调用这个函数。
|
|
285
|
+
|
|
286
|
+
> 注意:因为系统默认注册了 `应用/控制器/方法` 的全局路由,所以控制器里的方法都能被直接访问,如果想作为私有方法不被访问,可以在方法前加下划线`_`前缀。
|
|
287
|
+
|
|
279
288
|
### Middleware中间件类
|
|
280
289
|
|
|
281
290
|
> 方法:**$show(content)** 参照控制器类介绍
|
|
@@ -290,9 +299,9 @@ module.exports = Index;
|
|
|
290
299
|
|
|
291
300
|
### Db数据库类
|
|
292
301
|
|
|
293
|
-
> 方法:**async startTrans(
|
|
302
|
+
> 方法:**async startTrans(fun)** 开启事务
|
|
294
303
|
|
|
295
|
-
|
|
304
|
+
异步方法,如果`fun`不为空,并且是函数,则开启事务后,会自动执行此函数,并提交事务,不用再手工提交或回滚事务。`fun`为异步函数。
|
|
296
305
|
|
|
297
306
|
> 方法:**async commit()** 提交事务
|
|
298
307
|
|
|
@@ -727,9 +736,9 @@ module.exports = route;
|
|
|
727
736
|
|
|
728
737
|
应用配置不用每项都设置,只设置自己需要改的,默认会继承框架的默认配置,在控制器、中间件、模板类、模型类里都可以通过`this.$config.xxx`使用。
|
|
729
738
|
|
|
730
|
-
app配置`.config/app.js`:
|
|
739
|
+
- app配置`.config/app.js`:
|
|
731
740
|
```javascript
|
|
732
|
-
{
|
|
741
|
+
const app = {
|
|
733
742
|
app_debug: true, // 调试模式
|
|
734
743
|
app_multi: false, // 是否开启多应用
|
|
735
744
|
|
|
@@ -744,20 +753,22 @@ app配置`.config/app.js`:
|
|
|
744
753
|
|
|
745
754
|
koa_body: null // koa-body配置参数,为''、null、false时,关闭koa-body,此时upload类将不可用,并且系统接收不到post参数。启用的话,建议配置:{multipart: true, formidable: {keepExtensions: true, maxFieldsSize: 10 * 1024 * 1024}}
|
|
746
755
|
}
|
|
756
|
+
module.exports = app;
|
|
747
757
|
```
|
|
748
|
-
模板配置`.config/view.js`:
|
|
758
|
+
- 模板配置`.config/view.js`:
|
|
749
759
|
```javascript
|
|
750
|
-
{
|
|
760
|
+
const view = {
|
|
751
761
|
view_folder: 'view', // 模板目录名
|
|
752
762
|
view_depr: '/', // 模版文件名分割符,'/'代表二级目录
|
|
753
763
|
view_ext: '.htm', // 模版文件后缀
|
|
754
764
|
view_engine: 'art-template', // 默认模版引擎,字符串或引擎类,咱不支持更换
|
|
755
765
|
view_filter: {}, // 模版函数,配置注入模板的函数,默认会自动注入url函数
|
|
756
766
|
}
|
|
767
|
+
module.exports = view;
|
|
757
768
|
```
|
|
758
|
-
数据库配置`.config/db.js`:可以配置多个,方便程序里切换使用。
|
|
769
|
+
- 数据库配置`.config/db.js`:可以配置多个,方便程序里切换使用。
|
|
759
770
|
```javascript
|
|
760
|
-
{
|
|
771
|
+
const db = {
|
|
761
772
|
default: {
|
|
762
773
|
type : 'mysql', // 数据库类型
|
|
763
774
|
host : '127.0.0.1', // 服务器地址
|
|
@@ -769,24 +780,27 @@ app配置`.config/app.js`:
|
|
|
769
780
|
prefix : 'jj_' // 数据库表前缀
|
|
770
781
|
}
|
|
771
782
|
}
|
|
783
|
+
module.exports = db;
|
|
772
784
|
```
|
|
773
|
-
日志配置`.config/log.js`:log_handle可以自定义日志handle
|
|
785
|
+
- 日志配置`.config/log.js`:log_handle可以自定义日志handle
|
|
774
786
|
```javascript
|
|
775
|
-
{
|
|
787
|
+
const log = {
|
|
776
788
|
log_level: [], // [error, warning, info, debug, http, sql]
|
|
777
789
|
log_handle: function(msg, level) {console.log(`[${format('YY-mm-dd HH:ii:ss')}] [${level}] ${typeof msg == 'String' ? msg : JSON.stringify(msg)}`);} //function(msg, level) {}
|
|
778
790
|
}
|
|
791
|
+
module.exports = log;
|
|
779
792
|
```
|
|
780
|
-
缓存配置`.config/cache.js`:
|
|
793
|
+
- 缓存配置`.config/cache.js`:
|
|
781
794
|
```javascript
|
|
782
|
-
{
|
|
795
|
+
const cache = {
|
|
783
796
|
cache_time: 60 * 60 * 24, // 默认缓存时间(1天),为空或false则为10年
|
|
784
797
|
clear_time: undefined // (undefined: 一天清理一次, 0: 关闭自动清理, >0: 为自动清理周期)
|
|
785
798
|
}
|
|
799
|
+
module.exports = cache;
|
|
786
800
|
```
|
|
787
|
-
分页配置`.config/page.js`:
|
|
801
|
+
- 分页配置`.config/page.js`:
|
|
788
802
|
```javascript
|
|
789
|
-
{
|
|
803
|
+
const page = {
|
|
790
804
|
page_key : 'page', // 默认分页标识
|
|
791
805
|
key_origin : 'query', // query 或 params
|
|
792
806
|
page_size : 10, // 默认分页大小
|
|
@@ -810,22 +824,65 @@ app配置`.config/app.js`:
|
|
|
810
824
|
//渲染模版
|
|
811
825
|
template : '<div class="pagination"><ul class="page">${index}${prev}${list}${next}${end}</ul>${info}</div>'
|
|
812
826
|
}
|
|
827
|
+
module.exports = page;
|
|
813
828
|
```
|
|
814
|
-
跳转模板配置`.config/tpl.js`:模板可以配置为自定义的
|
|
829
|
+
- 跳转模板配置`.config/tpl.js`:模板可以配置为自定义的
|
|
815
830
|
```javascript
|
|
816
|
-
{
|
|
831
|
+
const tpl = {
|
|
817
832
|
jump: require('./tpl/jump'), // 跳转模板
|
|
818
833
|
exception: require('./tpl/exception') // 异常页面模板
|
|
819
834
|
}
|
|
835
|
+
module.exports = tpl;
|
|
820
836
|
```
|
|
821
|
-
自定义配置`.config/self.js`:自定义配置同样可以直接通过`this.$config.self`使用。
|
|
837
|
+
- 自定义配置`.config/self.js`:自定义配置同样可以直接通过`this.$config.self`使用。
|
|
822
838
|
```javascript
|
|
823
|
-
{
|
|
839
|
+
const self = {
|
|
824
840
|
option1: ''
|
|
825
841
|
option2: ''
|
|
826
842
|
...
|
|
827
843
|
}
|
|
844
|
+
module.exports = self;
|
|
845
|
+
```
|
|
846
|
+
- 路由配置`.config/routes.js`:
|
|
847
|
+
路由功能基于`@koa/router`开发,关于url匹配规则可以参考官方文档:[文档地址](https://www.npmjs.com/package/@koa/router)
|
|
848
|
+
|
|
849
|
+
> 本框架默认内置 `应用/控制器/方法` 的全局路由,即如果不需要定制url,可以直接访问,无需配置路由。
|
|
850
|
+
|
|
851
|
+
> 路由配置为一个数组,每一项为一条规则,项属性包含url规则,path访问路径,name规则名字,type路由类型。url一旦匹配,即会停止,如果需要继续向下匹配,需在程序内调用 `await this.$next();`,路由配置参考示例:
|
|
852
|
+
```javascript
|
|
853
|
+
routes = [
|
|
854
|
+
{url: '/', path: 'app/index/index2'}, // 访问'/',会定位到app应用的index控制的index2方法
|
|
855
|
+
{url: '/article/:id.html', path: 'app/article/article', name: 'article'}, // 访问'/article/123.html',会定位到app应用的article控制的article方法;通过this.ctx.params.id可以获取到参数id;通过article可以反编译网址,执行this.$url.build(':article', {id: 123}); 生成'/article/123.html'
|
|
856
|
+
{url: '/admin', path: 'app/admin/check', type: 'middleware'}, // 访问'/admin',会定位到app应用的admin中间件的check方法
|
|
857
|
+
{url: '/about', path: 'app/about/index', type: 'view'}, // 访问'/about',会定位到app应用的view模板目录about目录下的index.htm文件,并直接输出文件内容
|
|
858
|
+
{url: '/:cate/list_:page.html', path: 'cate/cate', name: 'cate_page'}, // 多参数分页示例
|
|
859
|
+
{url: '/hello', path: async (ctx, next) => {ctx.body = 'hello world, hello jj.js!';}} // 直接路由到函数
|
|
860
|
+
];
|
|
861
|
+
|
|
862
|
+
module.exports = routes;
|
|
863
|
+
```
|
|
864
|
+
> 注意:本路由配置示例前两条规则为常规用法,后面的非常规用法,不建议使用。如果是单应用模式,可以去掉path参数里的app。
|
|
865
|
+
|
|
866
|
+
### 应用入口文件
|
|
867
|
+
|
|
868
|
+
应用入口文件放在项目的根目录,名字可以任意改。
|
|
869
|
+
|
|
870
|
+
示例文件 `./server.js`
|
|
871
|
+
```javascript
|
|
872
|
+
const {app, Logger} = require('jj.js');
|
|
873
|
+
|
|
874
|
+
app.run(3000, '127.0.0.1', function(err){
|
|
875
|
+
!err && Logger.info('http server is ready on 3000');
|
|
876
|
+
});
|
|
828
877
|
```
|
|
878
|
+
其中`app`是一个koa实例,可以使用`use`方法添加应用级中间件,使用方法参考koa文档。`app`的`run`方法经过了封装,仅允许调用一次。其中`Logger`因为是一个静态类,所以可以直接调用`info`方法输出日志。
|
|
879
|
+
|
|
880
|
+
## 总结
|
|
881
|
+
|
|
882
|
+
通过以上文档,可以看到:
|
|
883
|
+
1. jj.js是一个轻量的mvc框架,几乎所有文件不使用就不会调用。
|
|
884
|
+
2. 同时也是个功能强大的框架,支持应用级、路由级、控制器级三级中间件。
|
|
885
|
+
3. 类库自动加载,只要是继承自`Context`的类库,都可以在方法内使用包含`$`前缀的属性自动加载其他资源。
|
|
829
886
|
|
|
830
887
|
## 应用案例
|
|
831
888
|
|
package/lib/db.js
CHANGED
|
@@ -66,10 +66,10 @@ class Db extends Context
|
|
|
66
66
|
if(pool.has(this._config)) {
|
|
67
67
|
pool.get(this._config).end(err => {
|
|
68
68
|
if(err) {
|
|
69
|
-
const message = '连接池销毁失败:' + err
|
|
69
|
+
const message = '连接池销毁失败:' + err.message;
|
|
70
70
|
this.$logger.sql(message);
|
|
71
71
|
this.$logger.error(message);
|
|
72
|
-
throw new Error(message);
|
|
72
|
+
throw new Error('DbError: ' + message);
|
|
73
73
|
} else {
|
|
74
74
|
pool.delete(this._config);
|
|
75
75
|
this.$logger.sql(`连接池销毁成功:{poolTotal: ${pool.size}}`);
|
|
@@ -95,10 +95,10 @@ class Db extends Context
|
|
|
95
95
|
return new Promise((resolve, reject) => {
|
|
96
96
|
p.getConnection((err, connection) => {
|
|
97
97
|
if(err) {
|
|
98
|
-
const message = '获取数据库连接失败:' + err
|
|
98
|
+
const message = '获取数据库连接失败:' + err.message;
|
|
99
99
|
this.$logger.sql(message);
|
|
100
100
|
this.$logger.error(message);
|
|
101
|
-
reject(new Error(message));
|
|
101
|
+
reject(new Error('DbError: ' + message));
|
|
102
102
|
} else {
|
|
103
103
|
this.$logger.sql(`获取数据库连接:{limit: ${p.config.connectionLimit}, all: ${p._allConnections.length}, acquiring: ${p._acquiringConnections.length}, free: ${p._freeConnections.length}, queue: ${p._connectionQueue.length}}`);
|
|
104
104
|
resolve(connection);
|
|
@@ -134,10 +134,10 @@ class Db extends Context
|
|
|
134
134
|
return new Promise((resolve, reject) => {
|
|
135
135
|
conn.beginTransaction(async (err) => {
|
|
136
136
|
if(err) {
|
|
137
|
-
const message = '开启事务失败:' + err
|
|
137
|
+
const message = '开启事务失败:' + err.message;
|
|
138
138
|
this.$logger.sql(message);
|
|
139
139
|
this.$logger.error(message);
|
|
140
|
-
reject(new Error(message));
|
|
140
|
+
reject(new Error('DbError: ' + message));
|
|
141
141
|
} else {
|
|
142
142
|
const message = '开启事务成功!';
|
|
143
143
|
this.$logger.sql(message);
|
|
@@ -195,11 +195,11 @@ class Db extends Context
|
|
|
195
195
|
return new Promise((resolve, reject) => {
|
|
196
196
|
conn.commit((err) => {
|
|
197
197
|
if(err) {
|
|
198
|
-
const message = '事务提交失败:' + err
|
|
198
|
+
const message = '事务提交失败:' + err.message;
|
|
199
199
|
this.$logger.sql(message);
|
|
200
200
|
this.$logger.error(message);
|
|
201
201
|
this.rollback();
|
|
202
|
-
reject(new Error(message));
|
|
202
|
+
reject(new Error('DbError: ' + message));
|
|
203
203
|
} else {
|
|
204
204
|
trans.delete(this.ctx);
|
|
205
205
|
const message = '事务提交成功!';
|
|
@@ -220,10 +220,10 @@ class Db extends Context
|
|
|
220
220
|
return new Promise((resolve, reject) => {
|
|
221
221
|
conn.query(sql, params, (err, data) => {
|
|
222
222
|
if(err) {
|
|
223
|
-
const message = '数据操作失败,
|
|
223
|
+
const message = '数据操作失败,' + err.message + "\r\nSQL:" + this.format(sql, params);
|
|
224
224
|
this.$logger.sql(message);
|
|
225
225
|
this.$logger.error(message);
|
|
226
|
-
reject(new Error(message));
|
|
226
|
+
reject(new Error('DbError: ' + message));
|
|
227
227
|
} else {
|
|
228
228
|
this.$logger.sql('数据操作成功,SQL:' + this.format(sql, params));
|
|
229
229
|
resolve(data);
|
|
@@ -506,7 +506,7 @@ class Db extends Context
|
|
|
506
506
|
if(!where[0]) {
|
|
507
507
|
const message = 'update方法必须传入where参数';
|
|
508
508
|
this.$logger.sql(message);
|
|
509
|
-
throw new Error(message);
|
|
509
|
+
throw new Error('DbError: ' + message);
|
|
510
510
|
}
|
|
511
511
|
params = params.concat(where[1]);
|
|
512
512
|
|
|
@@ -539,7 +539,7 @@ class Db extends Context
|
|
|
539
539
|
if(!where[0]) {
|
|
540
540
|
const message = 'delete方法必须传入where参数';
|
|
541
541
|
this.$logger.sql(message);
|
|
542
|
-
throw new Error(message);
|
|
542
|
+
throw new Error('DbError: ' + message);
|
|
543
543
|
}
|
|
544
544
|
params = params.concat(where[1]);
|
|
545
545
|
|
package/lib/router.js
CHANGED
|
@@ -9,7 +9,7 @@ if (cfg_routes) {
|
|
|
9
9
|
for(let item of cfg_routes){
|
|
10
10
|
item.method || (item.method = 'all');
|
|
11
11
|
if(!~methods.indexOf(item.method)) {
|
|
12
|
-
throw new Error(
|
|
12
|
+
throw new Error(`RouteError: 未知路由方法:${item.method}`);
|
|
13
13
|
}
|
|
14
14
|
if(typeof item.path === 'function'){
|
|
15
15
|
router[item.method](item.url, item.path);
|
package/lib/run.js
CHANGED
|
@@ -7,9 +7,13 @@ const compose = require('koa-compose');
|
|
|
7
7
|
const Logger = require('./logger');
|
|
8
8
|
|
|
9
9
|
async function run(ctx, next, control_type=cfg_app.controller_folder) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
if(cfg_app.app_debug) {
|
|
11
|
+
Logger.debug(ctx.request);
|
|
12
|
+
Logger.debug(`Router(${control_type}): {APP: '${ctx.APP}', CONTROLLER: '${ctx.CONTROLLER}', ACTION: '${ctx.ACTION}'}`);
|
|
13
|
+
Object.keys(ctx.params).length && Logger.debug({Params: ctx.params});
|
|
14
|
+
Object.keys(ctx.query).length && Logger.debug({Get: ctx.query});
|
|
15
|
+
Object.keys(ctx.request.body || {}).length && Logger.debug({Post: ctx.request.body});
|
|
16
|
+
}
|
|
13
17
|
|
|
14
18
|
// 设置根加载器
|
|
15
19
|
ctx.$ = loader('./', ctx);
|
|
@@ -26,12 +30,12 @@ async function run(ctx, next, control_type=cfg_app.controller_folder) {
|
|
|
26
30
|
|
|
27
31
|
// 应用、控制器、方法名字验证
|
|
28
32
|
if (~ctx.APP.indexOf('.') || ~ctx.CONTROLLER.indexOf('.') || ~ctx.ACTION.indexOf('.')) {
|
|
29
|
-
throw new Error(
|
|
33
|
+
throw new Error(`RunError: 应用、控制器或方法名字不合法!`);
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
if (ctx.APP[0] == '_' || ctx.APP[0] == '$' || !ctx._[ctx.APP]) {
|
|
33
37
|
if(cfg_app.app_debug) {
|
|
34
|
-
throw new Error(
|
|
38
|
+
throw new Error(`RunError: 应用:${ctx.APP}不存在!`);
|
|
35
39
|
} else {
|
|
36
40
|
return false;
|
|
37
41
|
}
|
|
@@ -39,7 +43,7 @@ async function run(ctx, next, control_type=cfg_app.controller_folder) {
|
|
|
39
43
|
|
|
40
44
|
if (!ctx._[ctx.APP][control_type]) {
|
|
41
45
|
if(cfg_app.app_debug) {
|
|
42
|
-
throw new Error(
|
|
46
|
+
throw new Error(`RunError: 目录:${ctx.APP}/${control_type}不存在!`);
|
|
43
47
|
} else {
|
|
44
48
|
return false;
|
|
45
49
|
}
|
|
@@ -49,7 +53,7 @@ async function run(ctx, next, control_type=cfg_app.controller_folder) {
|
|
|
49
53
|
if(control_type == cfg_view.view_folder) {
|
|
50
54
|
const content = await readFile(path.join(ctx._[ctx.APP][control_type].__node.path, ctx.CONTROLLER + cfg_view.view_depr + ctx.ACTION + cfg_view.view_ext));
|
|
51
55
|
if(!content) {
|
|
52
|
-
throw new Error(
|
|
56
|
+
throw new Error(`RunError: 模板文件:${ctx.APP}/${control_type}/${ctx.CONTROLLER + cfg_view.view_depr + ctx.ACTION + cfg_view.view_ext}不存在!`);
|
|
53
57
|
}
|
|
54
58
|
ctx.body = content;
|
|
55
59
|
return;
|
|
@@ -60,7 +64,7 @@ async function run(ctx, next, control_type=cfg_app.controller_folder) {
|
|
|
60
64
|
|
|
61
65
|
if (ctx.CONTROLLER[0] == '_' || ctx.CONTROLLER[0] == '$' || typeof Controller != 'function') {
|
|
62
66
|
if(cfg_app.app_debug) {
|
|
63
|
-
throw new Error(`class文件:${ctx.APP}/${control_type}/${ctx.CONTROLLER}不存在!`);
|
|
67
|
+
throw new Error(`RunError: class文件:${ctx.APP}/${control_type}/${ctx.CONTROLLER}不存在!`);
|
|
64
68
|
} else {
|
|
65
69
|
return false;
|
|
66
70
|
}
|
|
@@ -73,7 +77,7 @@ async function run(ctx, next, control_type=cfg_app.controller_folder) {
|
|
|
73
77
|
|
|
74
78
|
if (ctx.ACTION[0] == '_' || ctx.ACTION[0] == '$' || typeof $controller[action] != 'function') {
|
|
75
79
|
if(cfg_app.app_debug) {
|
|
76
|
-
throw new Error(`class方法:${ctx.APP}/${control_type}/${ctx.CONTROLLER}/${humpAction}不存在!`);
|
|
80
|
+
throw new Error(`RunError: class方法:${ctx.APP}/${control_type}/${ctx.CONTROLLER}/${humpAction}不存在!`);
|
|
77
81
|
} else {
|
|
78
82
|
return false;
|
|
79
83
|
}
|
|
@@ -93,18 +97,18 @@ async function run(ctx, next, control_type=cfg_app.controller_folder) {
|
|
|
93
97
|
return stack.concat(async (ctx, next) => {
|
|
94
98
|
const [METHOD = action, MIDDLEWARE = ctx.CONTROLLER, APP = ctx.APP] = middle.middleware.split('/').reverse();
|
|
95
99
|
if(!ctx._[APP]) {
|
|
96
|
-
throw new Error(
|
|
100
|
+
throw new Error(`RunError: 中间件应用:${APP}不存在!`);
|
|
97
101
|
}
|
|
98
102
|
if(!ctx._[APP]['middleware']) {
|
|
99
|
-
throw new Error(
|
|
103
|
+
throw new Error(`RunError: 中间件目录:${APP}/middleware不存在!`);
|
|
100
104
|
}
|
|
101
105
|
const Middleware = ctx._[APP]['middleware'][MIDDLEWARE];
|
|
102
106
|
if(!Middleware || typeof Middleware != 'function') {
|
|
103
|
-
throw new Error(
|
|
107
|
+
throw new Error(`RunError: 中间件文件:${APP}/middleware/${MIDDLEWARE}不存在!`);
|
|
104
108
|
}
|
|
105
109
|
const $middleware = new Middleware(ctx, next);
|
|
106
110
|
if(!$middleware[METHOD] || typeof $middleware[METHOD] != 'function') {
|
|
107
|
-
throw new Error(
|
|
111
|
+
throw new Error(`RunError: 中间件方法:${APP}/middleware/${MIDDLEWARE}/${METHOD}不存在!`);
|
|
108
112
|
}
|
|
109
113
|
|
|
110
114
|
// 执行中间件方法
|
package/lib/url.js
CHANGED
|
@@ -12,7 +12,7 @@ const {toLine} = require('./utils/str');
|
|
|
12
12
|
class Url extends Context
|
|
13
13
|
{
|
|
14
14
|
build(url='', vars, ext='', domain='') {
|
|
15
|
-
if(typeof vars != 'object') {
|
|
15
|
+
if(vars && typeof vars != 'object') {
|
|
16
16
|
[vars, ext, domain] = [{}, vars, ext];
|
|
17
17
|
}
|
|
18
18
|
const query = {...vars};
|
|
@@ -56,6 +56,7 @@ class Url extends Context
|
|
|
56
56
|
|
|
57
57
|
if(~url.indexOf(':')) {
|
|
58
58
|
url = url.replace(/\:([^./]+)/g, (match, key) => {
|
|
59
|
+
key = key.split('(')[0];
|
|
59
60
|
if(key in query) {
|
|
60
61
|
const value = query[key];
|
|
61
62
|
delete query[key];
|
package/lib/utils/error.js
CHANGED
|
@@ -1,29 +1,33 @@
|
|
|
1
|
+
const fs = require('./fs.js');
|
|
2
|
+
|
|
1
3
|
function parseError(err) {
|
|
2
|
-
let
|
|
3
|
-
stack = stack.replace(/(\r\n)
|
|
4
|
-
|
|
5
|
-
let msg = err.name;
|
|
4
|
+
let msg = err.message;
|
|
5
|
+
let stack = err.stack.replace(/(\r\n)/g, "\n").split("\n");
|
|
6
|
+
|
|
6
7
|
if(err.name == 'TemplateError') {
|
|
7
|
-
msg = stack.pop();
|
|
8
|
+
msg = 'TemplateError: ' + stack.pop();
|
|
9
|
+
stack = [stack.shift().replace('TemplateError:', "at")];
|
|
8
10
|
} else {
|
|
9
|
-
|
|
11
|
+
stack = stack.filter(text => {return /^ at /.test(text);});
|
|
10
12
|
}
|
|
11
|
-
|
|
13
|
+
|
|
14
|
+
const file_info = stack[0].split(' ').slice(-1)[0].replace(/(\()|(\))/g, '').split(':');
|
|
12
15
|
const column = parseInt(file_info.pop());
|
|
13
16
|
const row = parseInt(file_info.pop());
|
|
14
17
|
const file_path = file_info.join(':');
|
|
15
18
|
let begin = row - 10;
|
|
16
19
|
let end = row + 10;
|
|
20
|
+
let nth = 0;
|
|
17
21
|
let code = '';
|
|
18
22
|
if(begin < 0) {
|
|
19
23
|
end -= begin;
|
|
20
24
|
begin = 0;
|
|
21
25
|
}
|
|
22
|
-
if(file_path == 'anonymous') {
|
|
26
|
+
if(file_path == 'anonymous' || !file_path || !fs.isFileSync(file_path)) {
|
|
23
27
|
code = stack;
|
|
24
28
|
} else {
|
|
25
29
|
code = require('fs').readFileSync(file_path);
|
|
26
|
-
code = code.toString().split("\n");
|
|
30
|
+
code = code.toString().replace(/(\r\n)/g, "\n").split("\n");
|
|
27
31
|
if(begin < 0) {
|
|
28
32
|
end -= begin;
|
|
29
33
|
begin = 0;
|
|
@@ -36,9 +40,6 @@ function parseError(err) {
|
|
|
36
40
|
begin = 0;
|
|
37
41
|
}
|
|
38
42
|
code = code.slice(begin, end);
|
|
39
|
-
if(err.name == 'TemplateError') {
|
|
40
|
-
stack = [stack[0]];
|
|
41
|
-
}
|
|
42
43
|
nth = row - begin;
|
|
43
44
|
}
|
|
44
45
|
|
package/lib/view.js
CHANGED