Support mounted app as any argument to app.use()

fixes #2277
This commit is contained in:
Fabien Franzen 2014-08-02 11:41:35 +02:00 committed by Douglas Christopher Wilson
parent 8449f23f0d
commit 17cea29013
3 changed files with 71 additions and 28 deletions

View File

@ -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

View File

@ -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);
});

View File

@ -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(){