titbit-loader

    21.5.5 • Public • Published

    titbit-loader

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

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

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

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

    此扩展从一开始,不是为了开发单体复杂的软件准备的,只是为了解决在中小规模的应用上,可以方便组织代码结构。但是,做到上百个Model、几百个路由、上百个中间件组装成一个复杂的应用也没有问题。

    注意:使用全局__mid.js加载中间件,可以指定group选项,但是要注意因此导致的OPTIONS请求错误导致的跨域问题,这时候,要求OPTIONS也要针对不同分组添加对应的OPTIONS请求

    示例:

    全局__mid.js文件:

    module.exports = [
      {
        name : 'cors',
        group: ['abc', 'xyz']
      }
    
    ]
    const titbit = require('titbit')
    const tbloader = require('titbit-loader')
    
    let tbl = new tbloader()
    
    const app = new titbit()
    
    app.options('/xyz/*', async c => {}, {group:'xyz'})
    
    app.options('/abc/*', async c => {}, {group:'abc'})
    
    /**
     * ....
     * */
    
    app.run(1234)

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

    const titbit = require('titbit');
    const tbloader = require('titbit-loader');
    
    var app = new titbit({
      debug: true        
    });
    
    var 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上。

    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');
    
    var app = new titbit({
      debug: true        
    });
    
    
    var 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);

    直接把model挂载到app.service

    开启directModel选项,则会在初始化model时候把实例直接挂载到app.service。而在请求上下文中,则可以通过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');
    
    var app = new titbit({
      debug: true        
    });
    
    var tbl = new tbloader({
      //默认就是true,默认通过app.service.model可以获取。
      loadModel: true, 
      mdb: new pg.Pool(dbconfig),
      //直接挂载到app.service
      directModel: true
    });
    
    tbl.init(app);
    
    app.run(2022);

    指定主页文件

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

    const titbit = require('titbit')
    const tbloader = require('titbit-loader')
    
    var app = new titbit({
      debug: true
    })
    
    var tbl = new tbloader({
      //默认是home.js,并且只有GET请求,主页不允许其他请求
      homeFile : 'home.js',
    
      //如果要指定子目录的文件,则要使用这样的形式
      //homeFile : 'user/home.js'
    });
    
    tbl.init(app)
    
    app.run(2022)

    如果你不想让homeFile起作用,则只需要给一个空字符串。

    指定加载目录

    const titbit = require('titbit');
    const tbloader = require('titbit-loader');
    
    var app = new titbit({
      debug: true
    });
    
    var tbl = new tbloader({
      //相对于程序所在目录,相对路径会自动计算转换为绝对路径。
      //如果指定目录下没有对应目录,会自动创建controller、model、middleware
      appPath : 'app1'
    });
    
    tbl.init(app);
    
    app.run(2022);

    POST请求的路由参数

    默认在处理路由映射时,POST请求不会带有参数,如果需要传递参数,可以通过选项postArgs开启。

    const titbit = require('titbit');
    const tbloader = require('titbit-loader');
    
    var app = new titbit({
      debug: true
    });
    
    var tbl = new tbloader({
      postArgs: true
    });
    
    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
    })
    
    var 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参数。

    加载全局中间件时指定分组

    注意:只有在全局的__mid.js中才会有效,其他都会忽略。因为除了全局中间件,其他的目录下都已经确定了分组。

    module.exports = [
      {
        name : '@apilimit',
        group: ['/api', '/call', '/']
      }
    ]
    

    别忘了指定分组时,开头的/。

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

    比如,有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目录中的文件,如果不想导出,则可以命名文件开头加上!(英文符号)。这时候会忽略此文件。

    导出controller中的某些分组

    通过subgroup选项可以指定要加载哪些目录下的路由文件,注意这对在controller目录中的文件无效,这些文件是一定会加载的,只有在controller中的子目录才会有效,比如在controller中存在三个目录和文件:

    abc/ bcd/ xyz/ a.js
    

    如果只想加载xyz则可以这样做:

    var app = new titbit({
      debug: true
    });
    
    var tbl = new tbloader({
      subgroup: ['xyz']
    });
    
    tbl.init(app);

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

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

    Install

    npm i titbit-loader

    DownloadsWeekly Downloads

    459

    Version

    21.5.5

    License

    GPL-3.0

    Unpacked Size

    67.5 kB

    Total Files

    6

    Last publish

    Collaborators

    • ant-army