Socket
Socket
Sign inDemoInstall

titbit-loader

Package Overview
Dependencies
0
Maintainers
1
Versions
210
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

titbit-loader

loader for titbit framework


Version published
Maintainers
1
Weekly downloads
209
decreased by-27.43%

Weekly downloads

Readme

Source

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.service[mname]。

  • 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回到之前的模式。

Keywords

FAQs

Last updated on 04 Apr 2024

Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc