diff --git a/History.md b/History.md index 3c3bf8a5..96b64966 100644 --- a/History.md +++ b/History.md @@ -5,6 +5,7 @@ unreleased - accepts a file system path instead of a URL - requires an absolute path or `root` option specified * deprecate `res.sendfile` -- use `res.sendFile` instead + * support mounted app as any argument to `app.use()` * deps: qs@1.0.2 - Complete rewrite - Limits array length to 20 diff --git a/lib/application.js b/lib/application.js index b8b69c06..2214bf6c 100644 --- a/lib/application.js +++ b/lib/application.js @@ -3,6 +3,7 @@ */ var finalhandler = require('finalhandler'); +var flatten = require('./utils').flatten; var mixin = require('utils-merge'); var Router = require('./router'); var methods = require('methods'); @@ -16,6 +17,7 @@ var compileQueryParser = require('./utils').compileQueryParser; var compileTrust = require('./utils').compileTrust; var deprecate = require('depd')('express'); var resolve = require('path').resolve; +var slice = Array.prototype.slice; /** * Application prototype. @@ -149,34 +151,41 @@ app.handle = function(req, res, done) { * @api public */ -app.use = function use(path, fn) { - var mount_app; - var mount_path; +app.use = function use(fn) { + var offset = 0; + var path = '/'; + var self = this; - // check for .use(path, app) or .use(app) signature - if (arguments.length <= 2) { - mount_path = typeof path === 'string' - ? path - : '/'; - mount_app = typeof path === 'function' - ? path - : fn; + // default path to '/' + if (typeof fn !== 'function') { + offset = 1; + path = fn; + } + + var fns = flatten(slice.call(arguments, offset)); + + if (fns.length === 0) { + throw new TypeError('app.use() requires middleware functions'); } // setup router this.lazyrouter(); var router = this._router; - // express app - if (mount_app && mount_app.handle && mount_app.set) { - debug('.use app under %s', mount_path); - mount_app.mountpath = mount_path; - mount_app.parent = this; + fns.forEach(function (fn) { + // non-express app + if (!fn || !fn.handle || !fn.set) { + return router.use(path, fn); + } + + debug('.use app under %s', path); + fn.mountpath = path; + fn.parent = self; // restore .app property on req and res - router.use(mount_path, function mounted_app(req, res, next) { + router.use(path, function mounted_app(req, res, next) { var orig = req.app; - mount_app.handle(req, res, function(err) { + fn.handle(req, res, function (err) { req.__proto__ = orig.request; res.__proto__ = orig.response; next(err); @@ -184,13 +193,8 @@ app.use = function use(path, fn) { }); // mounted an app - mount_app.emit('mount', this); - - return this; - } - - // pass-through use - router.use.apply(router, arguments); + fn.emit('mount', self); + }); return this; }; @@ -414,7 +418,7 @@ methods.forEach(function(method){ this.lazyrouter(); var route = this._router.route(path); - route[method].apply(route, [].slice.call(arguments, 1)); + route[method].apply(route, slice.call(arguments, 1)); return this; }; }); @@ -433,7 +437,7 @@ app.all = function(path){ this.lazyrouter(); var route = this._router.route(path); - var args = [].slice.call(arguments, 1); + var args = slice.call(arguments, 1); methods.forEach(function(method){ route[method].apply(route, args); }); diff --git a/test/app.use.js b/test/app.use.js index 681a6a91..ee237c88 100644 --- a/test/app.use.js +++ b/test/app.use.js @@ -18,7 +18,7 @@ describe('app', function(){ it('should reject missing functions', function(){ var app = express(); - app.use.bind(app, 3).should.throw(/requires callback function/); + app.use.bind(app, 3).should.throw(/requires middleware function/); }) describe('.use(app)', function(){ @@ -84,6 +84,44 @@ describe('app', function(){ .get('/post/once-upon-a-time') .expect('success', done); }) + + it('should support mounted app anywhere', function(done){ + var cb = after(3, done); + var blog = express() + , other = express() + , app = express(); + + function fn1(req, res, next) { + res.setHeader('x-fn-1', 'hit'); + next(); + } + + function fn2(req, res, next) { + res.setHeader('x-fn-2', 'hit'); + next(); + } + + blog.get('/', function(req, res){ + res.end('success'); + }); + + blog.once('mount', function (parent) { + parent.should.equal(app); + cb(); + }); + other.once('mount', function (parent) { + parent.should.equal(app); + cb(); + }); + + app.use('/post/:article', fn1, other, fn2, blog); + + request(app) + .get('/post/once-upon-a-time') + .expect('x-fn-1', 'hit') + .expect('x-fn-2', 'hit') + .expect('success', cb); + }) }) describe('.use(middleware)', function(){