Enrouten:用于 Express 的路由(route)配置中间件(初始化与配置模块)https://github.com/paypal/express-enrouten
我觉得这称不上一个中间件,为什么!express或者说connect的中间件,至少要有截获和转发request的功能吧,所以这个最多成的上是对express原有route功能的增强。其实看了他的代码,我觉得非常熟悉,因为我的route代码也是这么设计的,哈哈。
我使用Nodejs的时候,有一点不爽就是,对于route的注册和实现是分离的,route的注册是在app.js里面,而route的实现是放在route.js里面的,很多时候,如果要添加一个route,就不得不同时修改2个文件,这不仅给开发,还给维护带来很大的不变,我觉得Enrouten这个小框架可能就是为了解决这个问题。先看看他的代码实例:
一、注册route,Enrouten支持2种注册route的方法,一个是指定一个目录,一个是指定一个route的定义集合。
// 第一种方法 enrouten(app).withRoutes({ directory: 'controllers' }); // 第二种方法 enrouten(app).withRoutes({ routes: [ { path: '/', method: 'GET', handler: require('./controllers/index') }, { path: '/foo', method: 'GET', handler: require('./controllers/foo') } ] });
对于第一种方法,Enrouten会遍历改目录下所有的js文件,假如那个js文件export出来的是一个函数且仅仅是一个函数,那么就调用那个函数,并且传入express的实例,那个,这个函数该怎么写呢:
module.exports = function (app) { app.get('/', function (req, res) { // ... }); };
明白了吧,这个函数就把原来在app.js代码,移到了route内部(在Enrouten里面应该叫做controller吧)
对于第二种方法,我再贴一遍代码:
// 第二种方法 enrouten(app).withRoutes({ routes: [ { path: '/', method: 'GET', handler: require('./controllers/index') }, { path: '/foo', method: 'GET', handler: require('./controllers/foo') } ] });
这个代码很有误导性,看上去,好像已/foo开头的route都定义在了./controllers/foo那个文件里面,通过这个定义,就可以把所有对foo的request都handle了。但可是,可但是实际上是什么呢?Enrouten只不过,遍历这个数组,对里面每个route定义,调用app[method](def.path, def.handler),说的再明白点,就是app.get("/foo", handler)。所以没什么大花头哦。
好,再来看看我自己的设计,我有个名为AbstractRoute的基类,里面有一个公共的函数addRequestHandlers,这个函数需要你传入express实例,然后会调用受保护的_addRequestHandlers工具函数,这个工具函数和Enrouten做的差不多,挨个遍历定义,然后帮你注册routes。
"use restrict"; _ = require('underscore') should = require('should') module.exports = class AbstractRoute #public: addRequestHandlers: (server)-> @_addRequestHandlers(server, []) #protected: _addRequestHandlers: (app, routeRequests)-> should.exist(app, routeRequests) for routeRequest in routeRequests method = app[routeRequest.method] || app.get method.call(app, routeRequest.url, _.bind(routeRequest.handler, @))
module.exports = class CerRoute extends AbstractRoute #public: addRequestHandlers: (@m_app) -> should.exist(@m_app) routeRequests = [ new RouteRequest("get", "/", @handleIndexPage) new RouteRequest("get", "/cer/:idType(group|product)/:id", @handleProductIndexPage) new RouteRequest("get", "/cer/:idType(group|product)/:id/index", @handleAjaxProductIndex) new RouteRequest("get", "/cer/:idType(group|product)/:id/topCrashCommands", @handleAjaxTopCrashCommands) new RouteRequest("get", "/cer/:idType(group|product)/:id/weeklyCrashCount", @handleAjaxWeeklyCrashCount) new RouteRequest("get", "/cer/:idType(group|product)/:id/weeklyCrashCountPerUser", @handleAjaxWeeklyCrashCountPerUser) new RouteRequest("get", "/cer/:idType(group|product)/:id/fixRate", @handleAjaxFixRate) new RouteRequest("get", "/cer/:idType(group|product)/:id/osBreakDown", @handleAjaxOsBreakDown) new RouteRequest("get", "/cer/:idType(group|product)/:id/bucket", @handleAjaxBucket) new RouteRequest("get", "/cer/:idType(group|product)/:id/realTimebucket", @handleAjaxRealTimebucket) #new RouteRequest("get", "/cer/:idType(group|product)/:id/dataMining", @handleAjaxDataMining) new RouteRequest("get", "/cer/:idType(group|product)/:id/sendReport", @handleAjaxSendReport) new RouteRequest("post", "/cer/:idType(group|product)/:id/postReport", @handleAjaxPostReport) new RouteRequest("get", "/cer/:idType(group|product)/:id/data/researchRate", @handleDataGetResearchRate) new RouteRequest("get", "/cer/:idType(group|product)/:id/data/fixRate", @handleDataGetFixRate) ] @_addRequestHandlers(@m_app, routeRequests)
RouteRequest又是一个类,很简单很简单:
should = require('should') module.exports = class RouteRequest constructor: (@method, @url, @handler)-> should.exist(@method, "parameter 'method' can not be null") should.exist(@url, "parameter 'url' can not be null") should.exist(@handler, "parameter 'handler' can not be null")
在app.js里面的调用也很简单:
cerRoute = new (require('./lib/route/CerRoute'))(); cerRoute.addRequestHandlers(server);
我这样设计有2个优点:
1. 面对对象
2. 把route的注册和实现放在一个类/文件里面,好管理,好维护。
缺点:
1. 没有考虑到,把这个AbstractRoute作为一个单独的module独立出来,变成一个框架 :)