22.6.5 • Published 21 days ago

titbit-loader v22.6.5

Weekly downloads
144
License
GPL-3.0
Repository
-
Last release
21 days ago

titbit-loader

针对titbit框架的自动加载工具,用于自动创建并加载controller以及middleware的场景。也可以自动加载model。

基于此可实现MVC或类MVC的结构,并可以快速开发接口,生成RESTFul风格的API,路由映射文件等操作。

默认情况,会在当前目录创建controller、middleware、model目录。之后就可以在controller中编写class。

titbit-loader只是做了应该手动设定路由和安排中间件的部分,把这部分自动化了,在服务运行后,titbit-loader的作用就结束了。

此扩展从一开始,不是为了开发单体复杂的软件准备的,只是为了解决在中小规模的应用上,可以方便组织代码结构。但是它现在功能十分强大,在保持健壮性的同时,可以加载的控制器、模型数量没有限制,并且针对开发过程中的问题和需求几乎都提供了解决方案。比如:

  • 指定加载哪些控制器。

  • 命名!开头的控制器、目录和model不加载,方便保留代码但封禁部分功能。

  • 命名 _ 开头的model不加载,这方便你在model目录引入共享模块。

  • 路由前缀。

  • 自动化编排中间件。

  • 根据TEST模式或DEV模式或正式环境决定加载哪些中间件。

  • 加载控制后如果存在init函数自动运行并传递指定的参数。

  • 加载Model可以根据是否为构造函数进行不同的加载过程。

  • 自定义控制器参数。

  • controller具备独立性,在内部启用的全局中间件不会扩展到整个应用,因此加载多个controller相互不会影响。


对应关系

  • controller中的目录和文件名称映射为路由。

  • model目录中的模块初始化后挂载到app.service上,文件名字即为属性名。

  • middleware目录中的中间件扩展在controller目录中通过__mid.js 或 js文件中的 __mid 函数通过配置的方式进行编排。

路由规则

默认它按照RESTful规范进行路由的加载过程,比如controller目录中存在admin/account.js文件,那么生成的路由和对应的类方法如下:

GET /admin/account/:id 对应于 async get(c) 方法,获取具体的账户信息

GET /admin/account/:id 对应于 async list(c) 方法,获取列表

POST /admin/account 对应于 async post(c) 方法,创建管理员用户

PUT /admin/account/:id 对应于 async put(c) 方法,更新用户信息

DELETE /admin/account/:id 对应于 async delete(c) 方法,删除用户

:id参数是account.js类中通过this.param指定的,这部分开发者可以自定义参数。

POST请求的路由参数

默认在处理路由映射时,POST请求表示创建资源,不会带有参数,如果需要传递参数,需要通过在controller类中使用this.postParam属性指定。

class api {

  constructor () {
    this.param = '/:name/:id'
    //给post请求添加参数路由。
    this.postParam = '/:name'
  }

  async get(c) {

  }

  async post (c) {

  }

}

module.exports = api

初始化选项中的mname用于指定在app.service哪个子对象上挂载model,在v22.0.0以后,默认直接挂载到app.service上。

使用titbit-loader需要先安装titbit框架:

const titbit = require('titbit');
const tbloader = require('titbit-loader');

const app = new titbit({
  debug: true        
});

let tbl = new tbloader();

tbl.init(app);

app.run(2022);

controller目录中class示例:

//假设存在文件test.js,那么路径就是/test开头。

'use strict';

class test {
  
  constructor () {
    //默认参数是this.param = '/:id'。
    //可以通过设置this.param来指定参数。
    //this.param = '/:name/:key';

    //this.param = '';表示不带参数。
  }

  /*
    对应HTTP请求类型,有同名小写的方法名称处理请求,可以不写,需要哪些请求就写哪些。
    这里只使用了GET、POST、DELETE请求。
  */

  async get(c) {
    c.res.body = 'test ok:' + c.param.id;
  }

  //注意POST请求表示创建资源,默认加载时是不带参数的,也就是发起POST请求对应的路由是/test。
  async post(c) {
    c.res.body = c.body;
  }

  async delete(c) {
    c.res.body = 'delete ok';
  }

}

module.exports = test;

加载model

默认加载的model的名字就是文件名,没有.js。并且都在app.service.model对象中。但是你可以传递mname选项更改model的名字,或者设置选项directModel为true让model文件直接挂载到app.service上。

如果模型文件不是一个构造函数,则仅仅把导出的实例返回,否则就会自动进行new操作并传递mdb参数。

目前,titbit-loader不支持ES6模块的导出,请使用exports或module.exports进行导出操作。

controller中不要写太复杂的业务逻辑,这部分你应该放在model中,对于model,如何封装,是否再分层都可以自定义。titbit-loader只是加载并放在app.service中,仅此而已。

const titbit = require('titbit');
const tbloader = require('titbit-loader');
const dbconfig = require('./dbconfig');

//postgresql数据库的扩展
const pg = require('pg');

let app = new titbit({
  debug: true        
});


let tbl = new tbloader({
  //默认就是true,默认通过app.service.model可以获取。
  loadModel: true, 
  //设置了mdb,在你的model文件中初始化时会传递此参数。
  mdb: new pg.Pool(dbconfig),
  //设置了mname,则要通过app.service.m获取。
  mname: 'm'
});

tbl.init(app);

app.run(2022);

如果导出模块不是构造函数,比如是一个object或箭头函数,此时就只是返回这个导出结果,但是如果它存在init属性并且是一个函数,则会执行一次init函数,并传递mdb参数。

model挂载到app.service

默认情况下,mname选项为null,这表示把初始化的model实例挂载到app.service。若要挂载到app.service的属性上,比如挂载到app.service.model上,则可以通过选项mname指定属性为model。在请求上下文中,可以通过c.service访问,c.service指向app.service。这种依赖注入方式在titbit框架的文档中有说明。

const titbit = require('titbit');
const tbloader = require('titbit-loader');
const dbconfig = require('./dbconfig');

//postgresql数据库的扩展
const pg = require('pg');

let app = new titbit({
  debug: true        
});

let tbl = new tbloader({
  //默认就是true,默认通过app.service.model可以获取。
  loadModel: true, 
  mdb: new pg.Pool(dbconfig),
});

tbl.init(app);

app.run(2022);

指定主页文件

你应该已经注意到了,因为文件要映射路径,所以,对于主页来说,需要添加的'/'路径是不能在文件名中体现的,所以需要指定一个文件,并添加get方法作为主页。

const titbit = require('titbit')
const tbloader = require('titbit-loader')

let app = new titbit({
  debug: true
})

let tbl = new tbloader({
  //只有GET请求,主页不允许其他请求
  homeFile : 'home.js',

  //如果要指定子目录的文件,则要使用这样的形式
  //homeFile : 'user/home.js'
});

tbl.init(app)

app.run(2022)

如果你不想让homeFile起作用,则只需要给一个空字符串,默认homeFile选项就是一个空字符串。

指定加载目录

const titbit = require('titbit');
const tbloader = require('titbit-loader');

let app = new titbit({
  debug: true
});

let tbl = new tbloader({
  //相对于程序所在目录,相对路径会自动计算转换为绝对路径。
  //如果指定目录下没有对应目录,会自动创建controller、model、middleware
  appPath : 'app1'
});

tbl.init(app);

app.run(2022);

加载中间件

middleware目录存放的是中间件模块,但是不会每个都加载,需要你在controller中进行设置,配置文件为mid.js。注意controller中的mid.js表示对全局开启中间件,controller中的子目录中存在__mid.js表示只对当前目录分组启用,所见即所得,简洁直观高效。

之所以能够按照分组加载执行,其本质不在于titbit-loader本身,而是titbit提供的中间件分组执行机制。因为titbit提供了路由分组功能,并且可以指定中间件严格匹配请求方法和路由名称,所以基于此开发扩展就变得很方便。

controller/:
    __mid.js    //对全局开启
    
    test.js

    api/:
      __mid.js  //只对/api分组启用
      ...

    user/:
      __mid.js  //只对/user分组启用
      ...

    ...

__mid.js示例:

//导出的必须是数组,数组中的顺序就是执行顺序,name是middleware目录中文件的名字,不需要带.js
module.exports = [
  {
    name : 'cors',
    //表示要在接收body数据之前执行
    pre: true
  },
  {
    name : 'apilimit'
  }
];

加载中间件类

如果你的中间件模块是需要new操作的,不是一个直接执行的中间件函数,则可以使用@指定,同时要提供一个middleware函数。

module.exports = [
  {
    //@开头表示模块是类,需要初始化,并且要提供middleware方法,
    //这时候加载时会自动初始化并加载middleware函数作为中间件,
    //并且会绑定this,你可以在中间件模块的middleware函数中比较放心的使用this。
    name : '@apilimit'
  }

];

直接指定中间件

在 v21.3.0版本开始,可以通过middleware属性直接指定中间件。

//文件__mid.js

let mt = async (c, next) => {
  console.log(`mt run ${(new Date()).toLocaleString()}`)
  await next()
}

module.exports = [
  {
    middleware: mt
  }
]

只加载model并指定model路径

可以通过modelPath设定model所在目录,并通过loadModel加载。

const titbit = require('titbit')
const tbloader = require('titbit-loader')

const app = new titbit({
  debug: true
})

let tbl = new tbloader({
  modelPath : 'dbmodel',
  //指定挂载到app.service.dm上,这会创建dm对象并进行挂载。
  mname : 'dm',
})

//只是加载model类。
tbl.loadModel(app)

app.run(1234)

指定中间件的加载环境

如果要区分开发模式还是发布模式,并根据不同情况加载中间件,可以使用mode属性,这个功能在v21.4.0开始支持。

mode有2个可选的值:test | dev。都表示在对应的开发环境才会加载。没有mode,则会直接加载,不做任何区分。

mode为 online 则表示只有在生产环境才会加载执行,开发测试模式不会加载。

这个属性只是指定了加载条件,而对于条件的检测,是titbit框架的实例的service.TEST 或者 service.DEV属性是否存在并为true。

//文件__mid.js 

let mt = async (c, next) => {
  console.log('dev test -- ', c.method, c.path, c.routepath)
  await next()
}

module.exports = [
  {
    name : 'api-log',
    mode : 'test'
  },

  {
    middleware : mt,
    mode : 'dev'
  },

  {
    name : 'api-limit',
    mode : 'online'
  }

]

这个功能是具备开发性质的,就是这需要你在titbit服务中,只要设置了以下配置:

const app = new titbit()

//相当于app.service.TEST = true
app.addService('TEST', true)

这就表示,会开启测试模式(开发模式)。这个时候,不仅titbit-loader会检测并确定是否加载中间件,还可以在请求上下文中知道应用运行在开发模式。

高级功能

这部分功能相对要麻烦点,但是可以应对比较复杂的情况。

分组的名称

如果通过输出测试可以看到中间件分组,只是比较麻烦,在titbit-loader加载时,采用了非常简单的机制,controller所在目录,即为根分组,名字是'/'。其他都是目录名字作为分组名称,但是都以/开头。

比如以下目录结构:

controller/:
  a.js
  ...
  api/:
    user.js
    ...
  admin/:
    user.js
    ...

a.js所在分组是/。user.js所在分组是/api,这样,不通过titbit-loader加载的中间件,也可以指定分组,可以对相关分组生效。

加载中间件时传递参数

对于中间件是class的情况,有时还需要传递参数,这时候,可以通过__mid.js中的args属性来指定:

module.exports = [
  {
    name : '@apilimit',
    args : {
      maxLimit: 100,
      timeout: 56000
    }
  }

]

这在初始化apilimit实例时,会传递args参数。

只对文件中的某些请求启用中间件

比如,有controller/a.js文件,只对其中的post和put请求启用限制body大小的中间件,则可以在class中提供__mid函数:

class a {

  constructor () {

  }

  async get (c) {
    //...
  }

  async post (c) {
    //...
  }

  async put (c) {
    //...
  }

  __mid () {
    return [
      {
        name : 'setMaxBody',
        pre: true,
        //只对post和put函数启用,而且只有请求/a路径时才会生效。
        path : ['post', 'put']
      }
    ]
  }

}

不导出controller和model

在controller和model目录中的文件,如果不想导出,则可以命名文件开头加上!(英文符号)。这时候会忽略此文件。对于model来说,以!和开头的文件都不会导出,以开头的文件可以作为model的公共模块。

导出controller中的某些分组

通过subgroup选项可以指定要加载哪些目录下的路由文件,注意这时候若要对controller目录中的文件也加载,要在subgroup数组中包括空字符串或 '/',比如在controller中存在三个目录和文件:

abc/ bcd/ xyz/ a.js

如果只想加载xyz 和 a.js则可以这样做:

const app = new titbit({
  debug: true
});

let tbl = new tbloader({
  subgroup: ['xyz', '']
});

tbl.init(app);

这时候会加载xyz目录中的文件以及a.js。

对于大规模应用来说,你最好是进行服务拆分,这个时候,titbit+titbit-loader组成一个服务处理业务,然后再把多个这样的应用组合完成更大规模的处理。



mdbMap 指定多个模型关系

比如,你要对接读写分离的数据库服务。可以这样使用:

'use strict'

const Titloader = require('titbit-loader')
const Titbit = require('titbit')
const pg = require('pg')

let readorm = new pg.Pool({
  host: '127.0.0.1',
  database: 'read',
  port:5432,
  user: 'pt',
  password: '222werrr'
})

let writeorm = new pg.Pool({
  host: '127.0.0.1',
  database: 'write',
  port: 5432,
  user: 'pt',
  password: '222werrr'
})

const app = new Titbit()

let tbl = new Titloader({
  loadModel: false,
  mdbMap: {
    read: {
      mdb: readorm
    },

    write: {
      mdb: writeorm
    }
  }
})

tbl.init(app)

app.run(1234)

mdbMap支持属性path指定不同的model目录,默认和普通model加载配置目录一致。mdb如果不设置则默认为null,不会使用全局mdb的配置。

mdbMap和mdb以及loadModel不冲突,如果不设置loadModel为false,仍然会加载model目录下的模型。

一些常量和service函数

在v22.1.2版本开始,加载完成后,会在app.service上添加几个常量:

  • __prepath__ 获取路由的前缀路径。

  • __appdir__ controller、model等目录所在的绝对路径。

  • __model__ 指向对应的模型对象,当mname为空,没有此属性,否则指向app.servicemname

  • modelMap(key: string)函数 获取mdbMap设定的key值指向的Model对象。

  • getModel(name, key='')函数 获取具体的Model实例,如果key值为空,则会在默认加载的Model上获取,否则会在mdbMap设定的加载关系上获取。

完整选项

选项说明默认值
appPath指定要加载的路径默认为调用扩展的文件所在路径。
controllerPath指定要加载的控制器目录默认为controller
modelPath指定要加载的模型目录默认为model
midwarePath指定中间件所在目录默认为middleware
optionsRoute是否自动设定OPTIONS路由默认为true
prepath路由前缀默认为空字符串
initArgscontroller中初始化类要传递的参数默认为null,表示不传递。
homeFile首页的文件默认为空字符串
modelNamePre模型挂载时,名字的前缀。默认为空字符串
mname模型所在的app.service上的属性名字默认为空,表示直接挂载到app.service上。
multi是否允许多次加载默认为false
mdbMapmodel映射关系默认为null, 如果需要指定不同model的多个服务,需要使用此选项。比如,读写分离的两个数据库服务。
fileAsGroup以文件作为路由分组从v22.3.0开始默认为true,设置为false回到之前的模式。
22.6.5

21 days ago

22.6.4

3 months ago

22.6.4-beta

3 months ago

22.6.4-beta2

3 months ago

22.6.4-beta3

3 months ago

22.6.4-beta4

3 months ago

22.6.3-beta2

5 months ago

22.6.3

5 months ago

22.6.2

5 months ago

22.6.1

5 months ago

22.6.0

5 months ago

22.6.2-beta2

5 months ago

22.6.2-beta3

5 months ago

22.6.2-beta

5 months ago

22.6.0-beta

5 months ago

22.6.3-beta

5 months ago

22.5.3-beta

7 months ago

22.5.3

7 months ago

22.5.1-beta

1 year ago

22.5.2

1 year ago

22.5.1

1 year ago

22.1.9-beta

2 years ago

22.4.2-beta

1 year ago

22.5.0-beta

1 year ago

22.3.0-beta8

1 year ago

22.3.0-beta6

1 year ago

22.3.0-beta7

1 year ago

22.3.0-beta4

1 year ago

22.3.0-beta5

1 year ago

22.3.0-beta2

1 year ago

22.3.0-beta3

1 year ago

22.4.6-beta

1 year ago

22.1.9

2 years ago

22.4.4-beta

1 year ago

22.4.5-beta

1 year ago

22.2.1

2 years ago

22.2.0

2 years ago

22.2.3

1 year ago

22.2.2

2 years ago

22.2.3-beta4

1 year ago

22.2.3-beta5

1 year ago

22.2.3-beta6

1 year ago

22.2.3-beta7

1 year ago

22.2.3-beta8

1 year ago

22.2.3-beta9

1 year ago

22.2.3-beta12

1 year ago

22.2.3-beta11

1 year ago

22.2.3-beta10

1 year ago

22.3.0

1 year ago

22.4.0-beta

1 year ago

22.2.3-beta2

1 year ago

22.2.3-beta3

1 year ago

22.3.0-beta

1 year ago

22.4.6

1 year ago

22.4.5

1 year ago

22.4.4

1 year ago

22.4.3

1 year ago

22.4.2

1 year ago

22.4.1

1 year ago

22.4.0

1 year ago

22.4.1-beta

1 year ago

22.4.3-beta

1 year ago

22.2.3-beta

1 year ago

22.5.0

1 year ago

22.3.0-beta13

1 year ago

22.3.0-beta12

1 year ago

22.3.0-beta11

1 year ago

22.3.0-beta10

1 year ago

22.1.6-beta

2 years ago

22.1.5-beta3

2 years ago

22.1.5-beta2

2 years ago

22.1.5-beta

2 years ago

22.1.8

2 years ago

22.1.7

2 years ago

22.1.6

2 years ago

22.1.5

2 years ago

22.1.4-beta

2 years ago

22.1.3-beta

2 years ago

22.1.0-beta

2 years ago

22.1.3-beta2

2 years ago

22.1.1-beta

2 years ago

22.1.2-beta

2 years ago

22.1.1-beta2

2 years ago

22.1.2

2 years ago

22.1.1

2 years ago

22.1.0

2 years ago

22.1.3

2 years ago

22.0.5

2 years ago

22.0.4

2 years ago

22.0.4-beta

2 years ago

22.0.1-beta5

3 years ago

22.0.1-beta4

3 years ago

22.0.1-beta6

3 years ago

22.0.1-beta3

3 years ago

22.0.1-beta2

3 years ago

21.7.0-beta2

3 years ago

22.0.0-beta

3 years ago

21.7.0-beta

3 years ago

22.0.3

3 years ago

22.0.2

3 years ago

22.0.1

3 years ago

22.0.0

3 years ago

21.6.3

3 years ago

21.6.2

3 years ago

21.6.1

3 years ago

21.6.3-beta4

3 years ago

21.6.3-beta2

3 years ago

21.6.3-beta

3 years ago

21.5.9-beta2

3 years ago

21.5.9-beta3

3 years ago

21.5.9-beta4

3 years ago

21.5.9

3 years ago

21.6.0

3 years ago

21.5.9-beta

3 years ago

21.5.7

3 years ago

21.5.6

3 years ago

21.5.8

3 years ago

21.5.6-beta3

3 years ago

21.5.6-beta4

3 years ago

21.5.6-beta2

3 years ago

21.5.8-beta

3 years ago

21.5.6-beta

3 years ago

21.5.3-beta

3 years ago

21.5.5

3 years ago

21.5.4

3 years ago

21.5.3

3 years ago

21.5.3-beta7

3 years ago

21.5.3-beta6

3 years ago

21.5.3-beta5

3 years ago

21.5.3-beta4

3 years ago

21.5.3-beta3

3 years ago

21.5.3-beta2

3 years ago

21.5.0-beta3

3 years ago

21.5.0-beta4

3 years ago

21.5.0-beta2

3 years ago

21.5.0-beta5

3 years ago

21.5.1

3 years ago

21.5.0

3 years ago

21.5.2

3 years ago

21.5.0-beta

3 years ago

21.4.5

3 years ago

21.4.4

3 years ago

21.4.3

3 years ago

21.4.2

3 years ago

21.4.1

3 years ago

21.4.0

3 years ago

21.4.0-beta2

3 years ago

21.4.0-beta

3 years ago

21.3.5

3 years ago

21.3.4

3 years ago

21.3.3

3 years ago

21.3.3-beta2

3 years ago

21.3.3-beta

3 years ago

21.3.2

3 years ago

21.3.1

3 years ago

21.3.0

3 years ago

21.2.0

3 years ago

21.1.0

3 years ago

21.0.5

3 years ago

21.0.4

4 years ago

21.0.3

4 years ago

21.0.2

4 years ago

21.0.1

4 years ago

21.0.0

4 years ago

20.6.6

4 years ago

20.6.3

4 years ago

20.6.2

4 years ago

20.6.5

4 years ago

20.6.4

4 years ago

20.6.2-beta

4 years ago

20.6.1-beta

4 years ago

20.6.1-beta2

4 years ago

20.6.1

4 years ago

20.5.21-beta

4 years ago

20.5.21

4 years ago

2.2.1

4 years ago

2.2.0

4 years ago

2.2.3

4 years ago

2.2.2

4 years ago

2.1.3

4 years ago

2.1.2

4 years ago

2.1.1

4 years ago

2.1.0

4 years ago

2.0.3

4 years ago

2.0.2

4 years ago

2.0.1

4 years ago

2.0.0

4 years ago

2.0.0-beta

4 years ago

1.3.2

4 years ago

1.3.1

4 years ago

1.3.0

4 years ago

1.2.7

4 years ago

1.2.6

4 years ago

1.2.5

4 years ago

1.2.4

4 years ago

1.2.4-beta

4 years ago

1.2.3-beta

4 years ago

1.2.2-beta

4 years ago

1.2.1-beta

4 years ago

1.2.0-beta

4 years ago

1.1.8

5 years ago

1.1.7

5 years ago

1.1.6

5 years ago

1.1.5

5 years ago

1.1.4

5 years ago

1.1.3

5 years ago

1.1.2

5 years ago

1.1.1

5 years ago

1.1.0

5 years ago

1.0.3

5 years ago

1.0.2

5 years ago

1.0.1

5 years ago

1.0.0

5 years ago