eggjs自動加載路由

剛接觸nodejs不久,在后端的web框架中,有接觸過express,koa,sails等一些框架。
express簡單,易上手,擴展也很強,但是寫法規(guī)范很多。
koa這次支持了es7的異步,終于可以擺脫回調(diào)的煩惱。
sails作為api服務(wù)端,功能很強大,但是文檔不是太多,當時候接觸的時候因為插件的版本號踩過很多坑,所以不是太喜歡。
eggjs,阿里出品,基于koa,整理了解之后感覺不錯選擇,文檔也比較齊全。
主要記錄eggjs中實際應用可能碰到的問題

快速開始

見官方文檔,就不再重復
eggjs官網(wǎng)文檔

路由

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中,會發(fā)現(xiàn),它的路由配置是通過配置文件控制的,如果不配置,也會自動去加載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');// 控制器的基礎(chǔ)路徑
    var files = fs.readdirSync(controllerPath)// 讀取文件
    var name, cObj, router;
    files.forEach(function (file) {
        name = file.replace('Controller.js', '')// 在controllers目錄下都是以Controller.js結(jié)尾命名的控制器,例如:UserController.js
        cObj = require(controllerPath + '/' + file.replace('.js', '')) // 動態(tài)加載控制器js

        router = express.Router()
        _initAction(name, cObj, router)// 初始化路由
        if (prefix) {// 是否統(tǒng)一添加路由前綴
            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: '創(chuàng)建'})
    },
    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 => {// 循環(huán)獲取到對應的路由控制器對象
        c = c[v]
    })
    keys.forEach(function (key) {
        if (key !== 'constructor') {// 去除掉構(gòu)造函數(shù)
            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)
}

總結(jié)

基本上可以通過這個方式實現(xiàn)自動加載路由配置實現(xiàn)。從這個中間還可以去擴展配置出類似與sails那樣的路由配置風格。
做這個東西就是為了偷懶,寫好之后,可以不用再去管路由配置問題,嗯,少了一些事,未對性能這塊進行測試,讀取加載應該影響不大,下次再看看是否可以封裝成插件,有時間再來研究~~~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容