上一篇分析了express的路由機制,此次主要補充一些沒有說到的東西。html
以前說到,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文件:web
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[7]); //輸出頂層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 = 404; 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 || 500); 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 || 500); res.render('error', { message: err.message, error: {} }); }); module.exports = app;
下面是./routes/index.js的代碼,在裏面新實例化了一個Router對象,而後在該對象上添加了5箇中間件express
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;
運行後的輸出以下:json
新建立的Router(這裏只是做爲一箇中間件): /*該Router對象中添加了5箇中間件,能夠看到第一個中間件的stack中有兩個元素,與上面代碼相對應,這裏用橙色標出*/數組
{ path: '/user',cookie
stack: 數據結構
[ { handle: [Function],app
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中。