原文来自个人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的测试用例。还不来尝试一下吗?