上一篇分析了express的路由机制,这次主要补充一些没有说到的东西。

  之前说到,Router是中间件容器,Route是路由中间件,他们各自维护一个stack数组,里面存放layer,layer是封装中间件的一个数据结构。其实Router中不仅能存放一般的中间件,还能存放Router,这一点在源码中能看的出来,因为Router的构造函数中返回的是一个router函数,而中间件的生成也需要一个处理程序(函数),那么如果把Router()返回的处理程序作为参数传入中间件的生成方法中,就相当于Router中存放了一个Router中间件,只不过这两个Router是两个不同的实例。app.Router是最顶层的,里面可以包含新的Router,新的Router是作为中间件添加到app.Router的,由于Router的结构设计,可以保证访问到里面每个中间件的处理程序,包括嵌套的Router里面的每个中间件。从express -e projectName命令生成的项目模型中就包括这种用法,下面是projectName项目根目录(不是express根目录)下的app.js文件:

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser'); var routes = require('./routes/index');
var users = require('./routes/users'); var fs = require('fs'); var app = express(); // view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs'); // uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev')); //从这里这里开始添加中间件,因为之前没添加过,所以会实例化一个Router,在实例化的时候会添加两个中间件(query和init),因此这个中间件是第三个
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public'))); app.use('/', routes); //routes是“./routes/index.js”文件中创建的Router实例,也就是通过这句代码在顶层Router里面添加了新的Router
app.use('/users', users); //var count = routes.stack.length;
var count = app._router.stack.count;
var content = "";
console.log(count);
/*for (var i = 0; i<count; i++) {
content+=dump_obj(routes.stack[i]) + "\r\n\r\n";
console.log(app._router.stack[i]);
};*/ console.log(app._router.stack[]); //输出顶层Router中的一个特殊中间件,即app.use('/',routes)语句添加的routes,该中间件也是Router对象 /*fs.writeFile('message.txt', content, function (err) {
if (err) throw err;
console.log('It\'s saved!');
}); function dump_obj(myObject) {
var s = "";
for (var property in myObject) {
s = s + "\n "+property +": " + myObject[property] ;
}
return s;
}*/
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = ;
next(err);
}); // error handlers // development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || );
res.render('error', {
message: err.message,
error: err
});
});
} // production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || );
res.render('error', {
message: err.message,
error: {}
});
}); module.exports = app;

下面是./routes/index.js的代码,在里面新实例化了一个Router对象,然后在该对象上添加了5个中间件

var express = require('express');
var router = express.Router(); //这里新实例化一个Router,直接调用app(application.js)中的app.use()或app[method]能保证在app中的Router实例只有一个,但是我们仍然可以再手动实例化新的Router,app.Router其实是最顶层的中间件容器,只能有一个 var users = {
'byvoid':{
name : "Carbo",
website : 'http://www.byvoid.com'
}
}; /*--------- test start 下面是测试代码,在新创建的Router中添加中间件,待会儿要输出进行验证 ---------*/
var route = router.route('/user');
route.get(function(req,res,next){},function(req,res){}); //第一个中间件添加了两个处理函数,下面的输出会进行验证 router.all('user/:username',function(req,res,next){
if(users[req.params.username]){
next();
}
else{
next(new Error(req.params.username+'does not exist.'));
}
}); router.get('/user/:username',function(req,res){
res.send(JSON.stringify(users[req.params.username]));
});
router.get('/user/:username',function(req,res){
res.send('user:' + req.params.username);
});
/*--------- test end -----------*/
 
/* GET home page. */
router.get('/', function(req, res) {
res.render('index', { title: 'Express' });
}); module.exports = router;

运行后的输出如下:

新创建的Router(这里只是作为一个中间件): /*该Router对象中添加了5个中间件,可以看到第一个中间件的stack中有两个元素,与上面代码相对应,这里用橙色标出*/

{ path: '/user',

stack:

[ { handle: [Function],

name: '<anonymous>',

params: undefined,

path: undefined,

keys: [],

regexp: /^\/?$/i,

method: 'get' },

{ handle: [Function],

name: '<anonymous>',

params: undefined,

path: undefined,

keys: [],

regexp: /^\/?$/i,

method: 'get' } ],

methods: { get: true } }

{ path: 'user/:username',

stack:

[ { handle: [Function],

name: '<anonymous>',

params: undefined,

path: undefined,

keys: [],

regexp: /^\/?$/i,

method: undefined } ],

methods: { _all: true } }

{ path: '/user/:username',

stack:

[ { handle: [Function],

name: '<anonymous>',

params: undefined,

path: undefined,

keys: [],

regexp: /^\/?$/i,

method: 'get' } ],

methods: { get: true } }

{ path: '/user/:username',

stack:

[ { handle: [Function],

name: '<anonymous>',

params: undefined,

path: undefined,

keys: [],

regexp: /^\/?$/i,

method: 'get' } ],

methods: { get: true } }

{ path: '/',

stack:

[ { handle: [Function],

name: '<anonymous>',

params: undefined,

path: undefined,

keys: [],

regexp: /^\/?$/i,

method: 'get' } ],

methods: { get: true } }

9

顶层Router(中间件容器): /*下面是顶层Router中的第7个中间件,该中间件也是个Router,蓝色部分是封装了子Router的Layer,子Router中的stack中有5个路由中间件,也就是上面输出的5个;子Router是handler(中间件处理程序)的属性值,也就是说这个子Router是作为普通中间件添加到app.Router中的*/

{ handle:

{ [Function: router]

params: {},

_params: [],

caseSensitive: undefined,

mergeParams: undefined,

strict: undefined,

stack: [ [Object], [Object], [Object], [Object], [Object] ] },

name: 'router',

params: undefined,

path: undefined,

keys: [],

regexp: { /^\/?(?=\/|$)/i fast_slash: true },

route: undefined }

  既然app.Router中可以添加Router对象,那么我们可以新建一个js文件,专门用来添加自定义的中间件,在该js文件中里面实例化一个Router对象,将路由的注册或非路由中间件的添加代码都写到该文件里,然后导出该Router对象,最后在app.js中将该Router添加到app.Router中。

express路由探析(续)的更多相关文章

  1. 深入探析koa之中间件流程控制篇

    koa被认为是第二代web后端开发框架,相比于前代express而言,其最大的特色无疑就是解决了回调金字塔的问题,让异步的写法更加的简洁.在使用koa的过程中,其实一直比较好奇koa内部的实现机理.最 ...

  2. 中文分词工具探析(二):Jieba

    1. 前言 Jieba是由fxsjy大神开源的一款中文分词工具,一款属于工业界的分词工具--模型易用简单.代码清晰可读,推荐有志学习NLP或Python的读一下源码.与采用分词模型Bigram + H ...

  3. 中文分词工具探析(一):ICTCLAS (NLPIR)

    1. 前言 ICTCLAS是张华平在2000年推出的中文分词系统,于2009年更名为NLPIR.ICTCLAS是中文分词界元老级工具了,作者开放出了free版本的源代码(1.0整理版本在此). 作者在 ...

  4. Emmet 语法探析

    Emmet 语法探析 Emmet(Zen Coding)是一个能大幅度提高前端开发效率的一个工具. 大多数编辑器都支持Snippet,即存储和重用一些代码块.但是前提是:你必须先定义 这些代码块. E ...

  5. 开源中文分词工具探析(三):Ansj

    Ansj是由孙健(ansjsun)开源的一个中文分词器,为ICTLAS的Java版本,也采用了Bigram + HMM分词模型(可参考我之前写的文章):在Bigram分词的基础上,识别未登录词,以提高 ...

  6. 开源中文分词工具探析(四):THULAC

    THULAC是一款相当不错的中文分词工具,准确率高.分词速度蛮快的:并且在工程上做了很多优化,比如:用DAT存储训练特征(压缩训练模型),加入了标点符号的特征(提高分词准确率)等. 1. 前言 THU ...

  7. 开源中文分词工具探析(五):FNLP

    FNLP是由Fudan NLP实验室的邱锡鹏老师开源的一套Java写就的中文NLP工具包,提供诸如分词.词性标注.文本分类.依存句法分析等功能. [开源中文分词工具探析]系列: 中文分词工具探析(一) ...

  8. Erlang调度器细节探析

    Erlang调度器细节探析 Erlang的很多基础特性使得它成为一个软实时的平台.其中包括垃圾回收机制,详细内容可以参见我的上一篇文章Erlang Garbage Collection Details ...

  9. 开源中文分词工具探析(五):Stanford CoreNLP

    CoreNLP是由斯坦福大学开源的一套Java NLP工具,提供诸如:词性标注(part-of-speech (POS) tagger).命名实体识别(named entity recognizer ...

随机推荐

  1. Python标准模块--ContextManager

    1 模块简介 在数年前,Python 2.5 加入了一个非常特殊的关键字,就是with.with语句允许开发者创建上下文管理器.什么是上下文管理器?上下文管理器就是允许你可以自动地开始和结束一些事情. ...

  2. 一起学微软Power BI系列-使用技巧(3)Power BI安卓手机版安装与体验

    Power BI有手机版,目前支持安卓,苹果和WP,不过没有WP手机,苹果在国内还不能用,要FQ和用就不测试了.安卓的我也也是费了九牛二虎之力才把app下载下来,把方法分享给大家. FQ太麻烦,所以建 ...

  3. .Net语言 APP开发平台——Smobiler学习日志:手机应用的TextTabBar快速实现方式

    参考页面: http://www.yuanjiaocheng.net/webapi/create-crud-api-1-put.html http://www.yuanjiaocheng.net/we ...

  4. Java程序:从命令行接收多个数字,求和并输出结果

    一.设计思想:由于命令行接收的是字符串类型,因此应先将字符串类型转化为整型或其他字符型,然后利用for循环求和并输出结果 二.程序流程图: 三.源程序代码:   //王荣荣 2016/9/23     ...

  5. java中if和switch哪个效率快

    首先要看一个问题,if 语句适用范围比较广,只要是 boolean 表达式都可以用 if 判断:而 switch 只能对基本类型进行数值比较.两者的可比性就仅限在两个基本类型比较的范围内.说到基本类型 ...

  6. FineBI:一个简单易用的自助BI工具

    过去,有关企业数据分析的重担都压在IT部门,传统BI分析更多面向的是具有IT背景的人员.但随着业务分析需求的增加,很多公司都希望为业务用户提供自助分析服务,将分析工作落实到业务人员手中.但同时,分析工 ...

  7. FineReport关于tomcat集群部署的方案

    多台服务器集群后,配置权限.数据连接.模板.定时调度等,只能每台服务器一个个配置,不会自动同步到所有服务器. 针对上述情况,在FineReport中提供新集群部署插件,将xml配置文件.finedb/ ...

  8. oracle SEQUENCE 创建, 修改,删除

    oracle创建序列化: CREATE SEQUENCE seq_itv_collection            INCREMENT BY 1  -- 每次加几个              STA ...

  9. Sublime Text 全程指引 by Lucida

    作者:Lucida 微博:@peng_gong 豆瓣:@figure9 博客园:@figure9 原文链接:http://zh.lucida.me/blog/sublime-text-complete ...

  10. Binder in Java

    Android在Native层实现了进程间的Binder通信,但是上层应用程序的开发及Framework的实现都是Java,用Java层再实现一次肯定是不合理的,Java可以通过JNI调用Native ...