eggjs自动加载路由

刚接触nodejs不久,在后端的web框架中,有接触过express,koa,sails等一些框架。
express简单,易上手,扩展也很强,但是写法规范很多。
koa这次支持了es7的异步,终于可以摆脱回调的烦恼。
sails作为api服务端,功能很强大,但是文档不是太多,当时候接触的时候因为插件的版本号踩过很多坑,所以不是太喜欢。
eggjs,阿里出品,基于koa,整理了解之后感觉不错选择,文档也比较齐全。
主要记录eggjs中实际应用可能碰到的问题

快速开始

见官方文档,就不再重复
eggjs官网文档

路由

eggjs路由是通过router.js这个文件来配置信息。
其中源码中有一段restful router api的代码:

resources(...args) {
    const splited = spliteAndResolveRouterParams({ args, app: this.app });
    const middlewares = splited.middlewares;
    // last argument is Controller object
    const controller = splited.middlewares.pop();

    let name = '';
    let prefix = '';
    if (splited.prefix.length === 2) {
      // router.get('users', '/users')
      name = splited.prefix[0];
      prefix = splited.prefix[1];
    } else {
      // router.get('/users')
      prefix = splited.prefix[0];
    }

    for (const key in REST_MAP) {
      const action = controller[key];
      if (!action) continue;

      const opts = REST_MAP[key];
      let formatedName;
      if (opts.member) {
        formatedName = inflection.singularize(name);
      } else {
        formatedName = inflection.pluralize(name);
      }
      if (opts.namePrefix) {
        formatedName = opts.namePrefix + formatedName;
      }
      prefix = prefix.replace(/\/$/, '');
      const path = opts.suffix ? `${prefix}/${opts.suffix}` : prefix;
      const method = Array.isArray(opts.method) ? opts.method : [ opts.method ];
      this.register(path, method, middlewares.concat(action), { name: formatedName });
    }

    return this;
  }

会去自动挂在restful router api的映射。
在学习sails中,会发现,它的路由配置是通过配置文件控制的,如果不配置,也会自动去加载controllers下的控制器映射到路由中。所以在当时候自己用express去尝试写了一个简单自动加载的机制。
代码如下:

var _initGlobals = function (app) {
    var models = app.get('models');
    for (var i in models) {
        global[tools.firstUpperCase(i)] = models[i]
    }
};
var _initAction = function (name, cObj, router) {
    var keys = Object.keys(cObj)
    if (actions) {// 映射普通action路由
        _initActionApi(cObj, keys, router)
    }
    if (rest) {// 映射rest 路由
        _initRestApi(cObj, keys, router)
    }
};
var _initActionApi = function (cObj, keys, router) {
    keys.forEach(function (key) {
        ['get', 'post', 'put', 'delete'].forEach(function (m) {
            router[m]('/' + key, function (req, res) {
                cObj[key](req, res)
            })
        })
    })
};
var _initRestApi = function (cObj, keys, router) {
    router.get('/', function (req, res) {
        // cObj.find(req, res)
        _.bind(cObj.find, this, req, res)();
    })
    router.post('/', function (req, res) {
        _.bind(cObj.create, this, req, res)();
    })
    router.get('/:id', function (req, res) {
        _.bind(cObj.findOne, this, req, res)();
    })
    router.put('/:id', function (req, res) {
        _.bind(cObj.update, this, req, res)();
    })
    router.delete('/:id', function (req, res) {
        _.bind(cObj.destroy, this, req, res)();
    })
};

var _init = function (app) {
    _initWSRouter(app);
    _initUpload(app);// 初始化通用的上传
    var controllerPath = path.join(process.cwd(), '/api', '/controllers');// 控制器的基础路径
    var files = fs.readdirSync(controllerPath)// 读取文件
    var name, cObj, router;
    files.forEach(function (file) {
        name = file.replace('Controller.js', '')// 在controllers目录下都是以Controller.js结尾命名的控制器,例如:UserController.js
        cObj = require(controllerPath + '/' + file.replace('.js', '')) // 动态加载控制器js

        router = express.Router()
        _initAction(name, cObj, router)// 初始化路由
        if (prefix) {// 是否统一添加路由前缀
            app.use('/' + prefix + '/' + name.toLowerCase(), router)
            // app.use('/' + name.toLowerCase(), router)
        } else {
            app.use('/' + name.toLowerCase(), router)
        }
    })

}


//api/controller/UserController.js
module.exports = {
    find: function (req, res) {
        res.json({type: 'find'})
    },
    findOne: function (req, res) {
        res.json({type: 'findOne'})
    },
    create: function (req, res) {
        res.json({type: '创建'})
    },
    update: function (req, res) {
        res.json({type: 'update'})
    },
    destroy: function (req, res) {
        res.json({type: 'destroy'})
    }
}

基于以上方案,所以正对于eggjs路由,一个想到的采用同样方式

简单自动装载

修改router.js使用上面方式。好,开始码代码...
...
终于码完了,测试完成,话不多说,代码如下

'use strict'
const fs = require('fs')
const path = require('path')
const _initAction = function (reqPath, obj, router, controller) {
    const keys = Object.getOwnPropertyNames(obj.prototype)
    let c = controller
    const l = reqPath.split('/')
    l.splice(0, 1)
    l.forEach(v => {// 循环获取到对应的路由控制器对象
        c = c[v]
    })
    keys.forEach(function (key) {
        if (key !== 'constructor') {// 去除掉构造函数
            const controllerMethod = c[key]
            if (controllerMethod) {// 在eggjs中,得到的keys会多出pathName,fullPath,所以过滤下
                ['get', 'post'].forEach(function (m) {// 定义路由的get,post,也可以扩展put,delete等
                    router[m](`${reqPath}/${key}`, controllerMethod)
                })
            }
        }
    })
    // 这个地方,挂在restful api,但是没有验证,是否会和上面action绑定有没有冲突,理论上命名不冲突,就不会有问题
    router.resources(l[l.length - 1], `${reqPath}`, c)
}
/**
 * @param {Egg.Application} app - egg application
 */
module.exports = app => {
    const {router, controller} = app
    const controllerPath = path.join(process.cwd(), '/app', '/controller/sys') // 这个地方应该去做递归把所有js文件遍历出来
    const files = fs.readdirSync(controllerPath)
    let reqPath
    let cObj
    files.forEach(function (file) {
        reqPath = controllerPath + '/' + file.replace('.js', '')
        /**
         *reqPath,用来通过controller中的路径作为请求路径
         *例如:文件路径:/app/controller/sys/user.js 那么得到就是/sys/user
        */
        reqPath = reqPath.substr(reqPath.indexOf('controller') + 10).replace(/\\/, '/')
        cObj = require(controllerPath + '/' + file.replace('.js', ''))
        _initAction(reqPath, cObj, router, controller)
    })
    // require('./router/sys/user')(app)
    // require('./router/sys/permissions')(app)
}

总结

基本上可以通过这个方式实现自动加载路由配置实现。从这个中间还可以去扩展配置出类似与sails那样的路由配置风格。
做这个东西就是为了偷懒,写好之后,可以不用再去管路由配置问题,嗯,少了一些事,未对性能这块进行测试,读取加载应该影响不大,下次再看看是否可以封装成插件,有时间再来研究~~~~

你可能感兴趣的:(eggjs自动加载路由)