eggjs自動加載路由

剛接觸nodejs不久,在后端的web框架中棒掠,有接觸過express孵构,koa,sails等一些框架烟很。
express簡單颈墅,易上手,擴展也很強溯职,但是寫法規(guī)范很多精盅。
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中,會發(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');// 控制器的基礎路徑
    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', '')) // 動態(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') {// 去除掉構造函數(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)
}

總結

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末这揣,一起剝皮案震驚了整個濱河市悔常,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌给赞,老刑警劉巖这嚣,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異塞俱,居然都是意外死亡,警方通過查閱死者的電腦和手機吏垮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進店門障涯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人膳汪,你說我怎么就攤上這事唯蝶。” “怎么了遗嗽?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵粘我,是天一觀的道長。 經常有香客問我,道長征字,這世上最難降的妖魔是什么都弹? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮匙姜,結果婚禮上畅厢,老公的妹妹穿的比我還像新娘。我一直安慰自己氮昧,他們只是感情好框杜,可當我...
    茶點故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著袖肥,像睡著了一般咪辱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上椎组,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天油狂,我揣著相機與錄音,去河邊找鬼庐杨。 笑死选调,一個胖子當著我的面吹牛,可吹牛的內容都是我干的灵份。 我是一名探鬼主播仁堪,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼填渠!你這毒婦竟也來了弦聂?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤氛什,失蹤者是張志新(化名)和其女友劉穎莺葫,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體枪眉,經...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡捺檬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了贸铜。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片堡纬。...
    茶點故事閱讀 38,643評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蒿秦,靈堂內的尸體忽然破棺而出烤镐,到底是詐尸還是另有隱情,我是刑警寧澤棍鳖,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布炮叶,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏镜悉。R本人自食惡果不足惜祟辟,卻給世界環(huán)境...
    茶點故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望积瞒。 院中可真熱鬧川尖,春花似錦、人聲如沸茫孔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缰贝。三九已至馍悟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間剩晴,已是汗流浹背锣咒。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留赞弥,地道東北人毅整。 一個月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像绽左,于是被迫代替她去往敵國和親悼嫉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,509評論 2 348

推薦閱讀更多精彩內容