原文來自個人blog
Mkbug.js
的前世此生 - 一款OOP
風格聲明式Nodejs
框架早在2014
年的時候第一次接觸Nodejs
,當時還在IBM
,而註明的Express.js
框架也是IBM
負責維護。因而理所固然的是用Express.js
開發Nodejs
應用。雖然國內對Koa
很是熱衷,可是我仍是有1000
個理由選擇Express.js
。成熟可靠的維護團隊和企業級應用背景,即便在2015
年IBM
將維護Express.js
交給了StrongeLoop
。
當時我所在的團隊負責一個內部工具系統開發,後臺使用Nodejs。可是有一天用戶反饋有一個線上問題。數據錯了。咱們立刻進行迴歸,發現線下是沒問題的。找了好久也沒找到緣由,後來從代碼發現這個分支是由一個環境變量做爲條件進行的分支處理。經過上線跟蹤日誌,發現實際讀取的環境變量不正確。php
原來上一次升級開發人員忘記了修改生產環境環境變量的設置致使的。git
一般咱們會將系統的配置信息配到一個Shell
腳本中。而後注入到環境變量中。因爲這些環境變量是在Docker
內生效。所以並不會影響其它程序,也不會引發衝突和覆蓋,而且環境遷移能力強。是一種很是常見的手動。
爲何咱們不能像Java
框架那樣,從配置文件中獲取呢?Java
和PHP
的框架都會有一個配置管理模塊去自動根據當前的模式獲取對應的配置信息,維護很是方便,很是智能。github
因而Mkbug.js
第一個核心模塊誕生了 ---- Config
。一個專門用來解決系統配置信息管理的模塊。通過多年的項目實踐和打磨,最終演變成了Mkbug.js
的核心模塊。express
// 目錄結構 ├── src ├── controller ├── ConfigTest.js ├── config ├── index.conf ├── index.dev.conf ├── index.js // src/config/index.conf TITLE=Mkbug.js Content=A OOP style declare Nodejs Web framework base on Express.js // src/config/index.dev.conf TITLE=Mkbug.js DEV // src/controller/ConfigTest.js const { BaseController, Config } = require('mkbugjs'); module.exports = class ConfigTest extends BaseController { getAction () { const conf = new Config('index') return conf } }
當咱們不設置process.env.NODE_ENV
啓動的時候,使用curl
請求接口返回的數據咱們發現,在特定環境下的配置信息會繼承沒有指定環境的配置信息。json
$ curl -XGET http://localhost:3001/api/configtest {"TITLE":"Mkbug.js","Content":"A OOP style declare Nodejs Web framework base on Express.js"}
而當咱們以process.env.NODE_ENV=dev
啓動的時候,使用curl
請求接口返回的數據咱們發現,在特定環境下的配置信息會繼承沒有指定環境的配置信息。api
$ curl -XGET http://localhost:3001/api/configtest {"TITLE":"Mkbug.js DEV","Content":"A OOP style declare Nodejs Web framework base on Express.js"}
Notice:當咱們沒有指定具體的運行環境的時候,Config
會默認加載與初始化參數名相同的conf
文件,好比index.conf
。可是當指定具體的環境(也就是process.env.NODE_ENV=dev
)後,將會用對應環境下的同名配置文件內容對默認配置內容進行覆蓋。好比本例中index.dev.conf
的內容會覆蓋index.conf
中相同的key對應的內容。
BaseController
路由抽象接口在離開IBM
後加入互聯網公司一直從事Nodejs
開發工做,互聯網項目高速迭代的特色讓Nodejs
項目的路由龐大,複雜且難以維護。時間久了,哪些路由信息還在使用?哪些已通過時不用了?模塊是否和路由設置一致?一個上百接口服務的路由虐的我痛不欲生。有人會說,你多拆幾個文件就好啦。但是文件越多。管理起來也越繁瑣。瀏覽器
因而第二個模塊在2016
年誕生了。也就是BaseController
的雛形RouterMgt
。能夠自動遍歷文件路徑,加載模塊,生成路由配置信息。本覺得一切都清淨了。可是看着Koa
的兼容新ES6
語法仍是內心癢癢。可是無奈與Nodejs 8.x
暫時還不支持那麼多語法。而團隊內也更傾向於Koa
支持新語法的誘惑。可是很快因爲公司業務發展不佳,公司解散了。服務器
再加入新公司後,Nodejs
已經發展到了10.x
。已經支持了99.7%
的新語法。而我須要的功能也都支持了。因而BaseController
完成了第二個版本。也就是如今Mkbug.js
核心模塊 ---- OOP風格聲明式路由管理模塊。閉包
// 目錄結構 ├── src ├── controller ├── _params ├── _id.js ├── pathtest ├── HelloWorld.js ├── index.js // src/controller/_params/_id.js const { BaseController } = require('mkbugjs'); module.exports = class IdTest extends BaseController { getAction () { return 'Hello ! this message from IdTest'; } } // src/controller/pathtest/HelloWorld.js const { BaseController } = require('mkbugjs'); module.exports = class HelloWorld extends BaseController { // 爲了不與上面的接口路徑衝突,因此在末尾增長了test getTestAction () { return 'Hello! this message from pathtest/hellowrold!'; } }
它並不像egg
或者thinkjs
以及其它一些Nodejs
框架那樣須要本身手動管理不少配置信息。一切都會幫你配置好。你僅僅須要實現BaseController
接口,那麼api
的路由信息就都幫你生成了。而且支持路由參數。瀏覽器請求結果:app
$ curl -XGET http://localhost:3001/helloworld/idtest Hello ! this message from IdTest $ curl -XGET http://localhost:3001/pathtest/helloworld/test Hello! this message from pathtest/hellowrold!
是否是很是簡單便捷?不再須要那麼冗餘的配置文件了。工程變得更加簡潔。這個模塊早期版本的輕量級,門檻低,低成本,易於維護的特色,在18
年落地了不少公司內部工具,更是在19
年成功幫助前東家的新業務線簽下兩份訂單。同時也經受住了大數據量訪問的考驗。同時提供了攔截器接口,能夠獲取接口響應時長等很是重要的信息。
在經歷了3
年多的打磨後,Mkbugjs
也基於該模塊誕生了。
Notice:由於Controller
的方法名是很是關鍵的配置信息,類的方法名必須以HTTP
協議的Methods
名開頭Action
結尾,這樣纔會被識別爲路由信息。若是在Methods
和Action
之間沒有其它單詞,則沒有對應的路徑。就像上面的例子同樣。Notice:
http
協議目前支持9個方法,固然,實際上不一樣瀏覽器還有更多的方法。可是爲了保持與標準同步,Mkbug.js
支持9種方法,分別是:GET
,HEAD
,POST
,PUT
,DELETE
,CONNECT
,'OPTIONS
,TRACE
,PATCH
。
Expressjs
尷尬的響應處理使用過Express.js
的開發者都知道,Express.js
必須顯示調用end
, json
, write
等api
去結束響應,不然客戶端會一直掛起,直至超時。而Mkbugjs
提供了統一的響應返回機制,即只須要return
,或者throw
一個MkbugError
異常對象,便可返回客戶端請求。也就避免了請求掛起。同時也統一了響應返回的標準。統一系統風格。
// src/controller/StatusTest.js const { BaseController, MkbugError } = require('mkbugjs'); module.exports = class StatusTest extends BaseController { getTestAction () { throw new MkbugError(500, 'Error Test') } }
執行結果以下:
$ curl -w " status=%{http_code}" localhost:3001/statustest/test Error Test status=500
Notice: 在Mkbug.js
中,任何實現BaseController
的接口,和BasePlugin
的中間件均可以經過這種方式自動響應客戶端請求。
BasePlugin
中間件抽象接口我帶過的不少人對JS
並非很是深刻,以致於常常把閉包,執行上下文寫錯。致使中間件常常出問題。而BasePlugin
只須要用戶實現本身的exec
接口邏輯,便可自動完成中間件的配置和執行操做。並不須要開發者過多的參與底層配置。
BasePlugin
提供一個exec
接口,該接口主要用於實現中間件的業務邏輯,並提供了res
和req
接口。當咱們須要攔截請求,只須要拋出MkbugError
異常便可。不然會執行下一步路由。
// 目錄結構 ├── src ├── controller ├── MiddleWare.js ├── plugin ├── TestMiddleware.js ├── index.js // src/plugin/TestMiddleware.js const { BasePlugin, MkbugError } = require('mkbugjs'); module.exports = class TestMiddleware extends BasePlugin { exec (req, res) { if (req.query.test === '1') { throw new MkbugError(200, 'Reject from TestMiddleware') } } } // src/controller/MiddleWare.js const { BaseController } = require('mkbugjs'); module.exports = class MiddleWare extends BaseController { getAction () { return 'Hello World' } }
這裏咱們建立了3
箇中間件,其中2
個對請求中query.test
等於1
和2
進行了攔截,分別返回200
狀態和401
狀態。
咱們先測試第一個中間件:
$ curl -w " status=%{http_code}" localhost:3001/api/MiddleWare?test=1 {"msg":"Reject from TestMiddleware1"} status=200
在這裏咱們能夠看到經過MkbugError
自定義返回的http
請求的status
和內容。而若是在中間件中什麼也不作的話:
$ curl -w " status=%{http_code}" localhost:3001/api/MiddleWare Hello World status=200
能夠看到對應的路由接口數據被正常返回。
Notice:這裏須要注意的是MkbugError
對象必須被throw
出來,而不是return
出來。
Mkbug.js
的誕生在2020年五一假期,由於疫情不能回家探親,一我的在居所無聊,忽然冒出一個想法,爲何不將這些有用的中間件組合成一個完整的框架呢?功能不亞於任何已有的Nodejs框架,並且風格更加新穎,也更接近於廣大JSer對ES6新語法的訴求。
因而花了五天時間,創造了Mkbug.js
:
// index.js const express = require('express'); const app = express(); const { Mkbug } = require('mkbugjs'); new Mkbug(app) .create('/') // 請求url前綴 .use(bodyParser.json()) // 使用express中間件 .start(3001, (err) => { // 啓動,同app.listen if (!err) console.log('Server started!') else console.error('Server start failed!') }) // src/controller/HelloWorld.js const { BaseController } = require('mkbugjs'); module.exports = class HelloWorld extends BaseController { getAction () { return 'Hello World'; } }
Notice:Mkbugjs
提供了豐富的Web
服務器經常使用的類。只須要繼承並實現對應的類,便可實現自動注入。就像Java
流行的Spring Boot
或者PHP
的Thinkphp
同樣。很是簡單。
我寫了這麼多你還沒心動嗎?目前安裝量已經超過1400次/月。並完成了全部case的測試用例。還不來嘗試一下嗎?