当前位置: 首页 > 后端技术 > Node.js

`Mkbug.js`的前世今生——一个`OOP`风格的声明式`Nodejs`框架

时间:2023-04-03 20:19:05 Node.js

第一次接触Nodejs的时候还在IBM,还维护着markedExpress.js框架由IBM。所以使用Express.js开发Nodejs应用是水到渠成的事情。虽然国内对Koa很热衷,但我还是有1000个理由选择Express.js。成熟可靠的维护团队和企业级应用背景,甚至在2015年,IBM就将Express.js的维护交给了StrongeLoop。这一切都始于一个在线故障。当时我的团队负责一个内部工具系统的开发,后台使用的是Nodejs。但是有一天,用户报告了一个在线问题。数据有误。我们马上返回,发现下线是没有问题的。找了半天也没找到原因。后来从代码中发现,这个分支是通过一个环境变量作为条件进行处理的。通过在线跟踪日志,发现实际读取的环境变量是错误的。原来是上次升级开发者忘了修改生产环境环境变量的设置。通常我们会将系统配置信息配置到一个shell脚本中。然后注入到环境变量中。因为这些环境变量是在Docker内部生效的。因此不会影响其他程序,不会造成冲突和覆盖,具有很强的环境迁移能力。是一个很常见的手册。为什么我们不能像Java框架那样从配置文件中获取呢?Java和PHP的框架都会有配置管理模块,根据当前模式自动获取相应的配置信息,维护起来非常方便和智能。于是Mkbug.js的第一个核心模块就诞生了——Config。专门解决系统配置信息管理的模块。经过多年的项目实践和打磨,最终演变成Mkbug.js的核心模块。//目录结构├──src├──controller├──ConfigTest.js├──config├──index.conf├──index.dev.conf├──index.js//src/config/index.jsconfTITLE=Mkbug.jsContent=AOOP风格声明基于Express.js的NodejsWeb框架//src/config/index.dev.confTITLE=Mkbug.jsDEV//src/controller/ConfigTest.jsconst{BaseController,配置}=require('mkbugjs');module.exports=classConfigTestextendsBaseController{getAction(){constconf=newConfig('index')returnconf}}当我们启动时不设置process.env.NODE_ENV,利用curl请求接口返回的数据,我们发现即特定环境下的配置信息会继承未指定环境下的配置信息。$curl-XGEThttp://localhost:3001/api/configtest{"TITLE":"Mkbug.js","Content":"AOOPstyledeclareNodejsWebframeworkbasedonExpress.js"}当我们使用process.env.NODE_ENV=dev启动时,使用curl请求接口返回的数据。我们发现特定环境下的配置信息会继承未指定环境的配置信息。$curl-XGEThttp://localhost:3001/api/configtest{"TITLE":"Mkbug.jsDEV","Content":"AOOPstyledeclareNodejsWebframeworkbasedonExpress.js"}注意:当我们当没有指定具体的运行环境时,Config会默认加载与初始化参数同名的conf文件,如index.conf。但是当指定了特定的环境(即process.env.NODE_ENV=dev)时,默认的配置内容会被相应环境下的同名配置文件的内容覆盖。例如本例中index.dev.conf的内容会覆盖index.conf中相同key对应的内容。让繁琐的配置走开——BaseController路由抽象接口离开IBM后加入一家互联网公司,一直从事Nodejs开发。互联网项目的高速迭代,使得Nodejs项目的路由庞大、复杂、难以维护。时间长了,哪些路由信息还在用?哪些已经过时了?模块是否与路由设置一致?一个有几百个接口服务的路由器,虐死我了。有人会说,你多拆几个文件就行了。但是文件比较多。管理起来也比较麻烦。于是2016年诞生了第二个模块,即RouterMgt,BaseController的雏形。可以自动遍历文件路径,加载模块,生成路由配置信息。我以为一切都很干净。但是看Koa对ES6新语法的兼容性,我还是心痒痒的。但无奈的是,Nodejs8.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.jsconst{BaseController}=require('mkbugjs');module.exports=classIdTestextendsBaseController{getAction(){return'你好!来自IdTest的消息';}}//src/controller/pathtest/HelloWorld.jsconst{BaseController}=require('mkbugjs');module.exports=classHelloWorldextendsBaseController{//为了避免和上面的接口路径冲突,在最后添加了testgetTestAction(){return'Hello!此消息来自pathtest/hellowrold!';它不需要像egg或thinkjs和其他一些Nodejs框架那样手动管理大量的配置信息。一切都将为您配置。你只需要实现BaseController接口,就会为你生成api的路由信息??。并且支持路由参数。浏览器请求结果:$curl-XGEThttp://localhost:3001/helloworld/idtest你好!此消息来自IdTest$curl-XGEThttp://localhost:3001/pathtest/helloworld/test你好!此消息来自pathtest/hellorold!是不是很简单方便呢?不再需要这些冗余的配置文件了。工程变得更加简洁。该模块早期版本轻量级、低门槛、低成本、易维护。2018年,实施了许多内部工具。2019年,成功帮助原东家新业务线签下两笔订单。同时经受住了大数据量访问的考验。同时提供拦截器接口,可以获取接口的响应时间等非常重要的信息。经过3年多的打磨,Mkbugjs也是基于这个模块诞生的。注意:因为Controller的方法名是非常关键的配置信息,类的方法名必须以HTTP协议的方法名开头,以Action结尾,这样才会被识别为路由信息。如果Methods和Action之间没有其他的话,则没有相应的路径。就像上面的例子一样。注意:http协议目前支持9种方法。当然,不同浏览器的方法更多。但是为了跟上标准,Mkbug.js支持9种方法,分别是:GET、HEAD、POST、PUT、DELETE、CONNECT、'OPTIONS、TRACE、PATCH。Expressjs的尴尬响应处理用过Express.js的开发者都知道,Express.js必须显式调用end、json、write等API来结束响应,否则客户端会一直挂到超时。并且Mkbugjs提供了统一的response返回机制,即你只需要返回或者抛出一个MkbugError异常对象就可以返回客户端请求。这也可以防止请求挂起。同时,响应返回的标准也统一了。统一的系统风格。//src/controller/StatusTest.jsconst{BaseController,MkbugError}=require('mkbugjs');module.exports=classStatusTestextendsBaseController{getTestAction(){thrownewMkbugError(500,'ErrorTest')}}执行结果如下:$curl-w"status=%{http_code}"localhost:3001/statustest/testErrorTeststatus=500注意:在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.jsconst{BasePlugin,MkbugError}=要求('mkbugjs');module.exports=classTestMiddlewareextendsBasePlugin{exec(req,res){if(req.query.test==='1'){thrownewMkbugError(200,'RejectfromTestMiddleware')}}}//src/controller/MiddleWare.jsconst{BaseController}=require('mkbugjs');module.exports=classMiddleWareextendsBaseController{getAction(){return'HelloWorld'}}这里创建了3个中间件,其中两个拦截请求中等于1和2的query.test,分别返回200状态和401状态.我们先测试第一个中间件:$curl-w"status=%{http_code}"localhost:3001/api/MiddleWare?test=1{"msg":"RejectfromTestMiddleware1"}status=200在这里我们可以看到状态以及自定义MkbugError返回的http请求内容。而中间件什么都不做:$curl-w"status=%{http_code}"localhost:3001/api/MiddleWareHelloWorldstatus=200可以看到相应路由接口数据正常返回。注意:这里需要注意的是,MkbugError对象必须抛出,不能返回。mkbug.js诞生于2020年的五一假期,因为疫情不能回家探亲。我一个人呆在家里很无聊。灵机一动,为什么不把这些好用的中间件组合成一个完整的框架呢?功能上不亚于任何现有的Nodejs框架,风格上也更加新颖,更贴近广大JSers对ES6新语法的诉求。所以创建Mkbug.js花了五天时间://index.jsconstexpress=require('express');constapp=express();const{Mkbug}=require('mkbugjs');newMkbug(app).create('/')//请求url前缀。use(bodyParser.json())//使用express中间件。start(3001,(err)=>{//开始,与app.listen相同if(!err)console.log('Serverstarted!')elseconsole.error('Serverstartfailed!')})//src/controller/HelloWorld.jsconst{BaseController}=require('mkbugjs');模块。exports=classHelloWorldextendsBaseController{getAction(){return'HelloWorld';}}注意:Mkbugjs提供了丰富的web服务器常用类。只需要继承并实现相应的类即可实现自动注入。就像Java流行的SpringBoot或者PHP的Thinkphp。很简单。到头来,我写了这么多,你还不心动吗?目前安装量已超过1400次/月。并完成了所有案例的测试用例。你为什么不试试呢?Github文档