2016-05-03 22:50:21 +00:00
|
|
|
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
|
|
|
|
// Node module: loopback
|
|
|
|
// This file is licensed under the MIT License.
|
|
|
|
// License text available at https://opensource.org/licenses/MIT
|
|
|
|
|
2014-11-11 16:51:50 +00:00
|
|
|
var assert = require('assert');
|
Middleware phases - initial implementation
Modify the app and router implementation, so that the middleware is
executed in order defined by phases.
Predefined phases:
'initial', 'session', 'auth', 'parse', 'routes', 'files', 'final'
Methods defined via `app.use`, `app.route` and friends are executed
as the first thing in 'routes' phase.
API usage:
app.middleware('initial', compression());
app.middleware('initial:before', serveFavicon());
app.middleware('files:after', loopback.urlNotFound());
app.middleware('final:after', errorHandler());
Middleware flavours:
// regular handler
function handler(req, res, next) {
// do stuff
next();
}
// error handler
function errorHandler(err, req, res, next) {
// handle error and/or call next
next(err);
}
2014-11-05 19:07:58 +00:00
|
|
|
var express = require('express');
|
|
|
|
var merge = require('util')._extend;
|
2014-12-10 08:36:45 +00:00
|
|
|
var mergePhaseNameLists = require('loopback-phase').mergePhaseNameLists;
|
Middleware phases - initial implementation
Modify the app and router implementation, so that the middleware is
executed in order defined by phases.
Predefined phases:
'initial', 'session', 'auth', 'parse', 'routes', 'files', 'final'
Methods defined via `app.use`, `app.route` and friends are executed
as the first thing in 'routes' phase.
API usage:
app.middleware('initial', compression());
app.middleware('initial:before', serveFavicon());
app.middleware('files:after', loopback.urlNotFound());
app.middleware('final:after', errorHandler());
Middleware flavours:
// regular handler
function handler(req, res, next) {
// do stuff
next();
}
// error handler
function errorHandler(err, req, res, next) {
// handle error and/or call next
next(err);
}
2014-11-05 19:07:58 +00:00
|
|
|
var debug = require('debug')('loopback:app');
|
2014-12-15 07:14:26 +00:00
|
|
|
var stableSortInPlace = require('stable').inplace;
|
2014-12-10 08:36:45 +00:00
|
|
|
|
|
|
|
var BUILTIN_MIDDLEWARE = { builtin: true };
|
Middleware phases - initial implementation
Modify the app and router implementation, so that the middleware is
executed in order defined by phases.
Predefined phases:
'initial', 'session', 'auth', 'parse', 'routes', 'files', 'final'
Methods defined via `app.use`, `app.route` and friends are executed
as the first thing in 'routes' phase.
API usage:
app.middleware('initial', compression());
app.middleware('initial:before', serveFavicon());
app.middleware('files:after', loopback.urlNotFound());
app.middleware('final:after', errorHandler());
Middleware flavours:
// regular handler
function handler(req, res, next) {
// do stuff
next();
}
// error handler
function errorHandler(err, req, res, next) {
// handle error and/or call next
next(err);
}
2014-11-05 19:07:58 +00:00
|
|
|
|
|
|
|
var proto = {};
|
|
|
|
|
|
|
|
module.exports = function loopbackExpress() {
|
|
|
|
var app = express();
|
|
|
|
app.__expressLazyRouter = app.lazyrouter;
|
|
|
|
merge(app, proto);
|
|
|
|
return app;
|
|
|
|
};
|
|
|
|
|
2014-11-11 16:51:50 +00:00
|
|
|
/**
|
|
|
|
* Register a middleware using a factory function and a JSON config.
|
|
|
|
*
|
|
|
|
* **Example**
|
|
|
|
*
|
|
|
|
* ```js
|
|
|
|
* app.middlewareFromConfig(compression, {
|
|
|
|
* enabled: true,
|
|
|
|
* phase: 'initial',
|
2014-11-13 12:22:25 +00:00
|
|
|
* params: {
|
2014-11-11 16:51:50 +00:00
|
|
|
* threshold: 128
|
|
|
|
* }
|
|
|
|
* });
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @param {function} factory The factory function creating a middleware handler.
|
|
|
|
* Typically a result of `require()` call, e.g. `require('compression')`.
|
2014-11-13 12:30:44 +00:00
|
|
|
* @options {Object} config The configuration.
|
Scope app middleware to a list of paths
Add a new argument to `app.middleware` allowing developers
to restrict the middleware to a list of paths or regular expresions.
Modify `app.middlewareFromConfig` to pass `config.paths` as the second
arg of `app.middleware`.
Examples:
// A string path (interpreted via path-to-regexp)
app.middleware('auth', '/admin', ldapAuth);
// A regular expression
app.middleware('initial', /^\/~(admin|root)/, rejectWith404);
// A list of scopes
app.middleware('routes', ['/api', /^\/assets/.*\.json$/], foo);
// From config
app.middlewareFromConfig(
handlerFactory,
{
phase: 'initial',
paths: ['/scope', /^\/(a|b)/]
});
2014-11-19 11:03:47 +00:00
|
|
|
* @property {String} phase The phase to register the middleware in.
|
2014-11-13 12:30:44 +00:00
|
|
|
* @property {Boolean} [enabled] Whether the middleware is enabled.
|
|
|
|
* Default: `true`.
|
|
|
|
* @property {Array|*} [params] The arguments to pass to the factory
|
|
|
|
* function. Either an array of arguments,
|
|
|
|
* or the value of the first argument when the factory expects
|
|
|
|
* a single argument only.
|
Scope app middleware to a list of paths
Add a new argument to `app.middleware` allowing developers
to restrict the middleware to a list of paths or regular expresions.
Modify `app.middlewareFromConfig` to pass `config.paths` as the second
arg of `app.middleware`.
Examples:
// A string path (interpreted via path-to-regexp)
app.middleware('auth', '/admin', ldapAuth);
// A regular expression
app.middleware('initial', /^\/~(admin|root)/, rejectWith404);
// A list of scopes
app.middleware('routes', ['/api', /^\/assets/.*\.json$/], foo);
// From config
app.middlewareFromConfig(
handlerFactory,
{
phase: 'initial',
paths: ['/scope', /^\/(a|b)/]
});
2014-11-19 11:03:47 +00:00
|
|
|
* @property {Array|string|RegExp} [paths] Optional list of paths limiting
|
|
|
|
* the scope of the middleware.
|
2014-11-13 12:22:25 +00:00
|
|
|
*
|
|
|
|
* @returns {object} this (fluent API)
|
2014-11-13 12:30:44 +00:00
|
|
|
*
|
|
|
|
* @header app.middlewareFromConfig(factory, config)
|
2014-11-11 16:51:50 +00:00
|
|
|
*/
|
|
|
|
proto.middlewareFromConfig = function(factory, config) {
|
|
|
|
assert(typeof factory === 'function', '"factory" must be a function');
|
|
|
|
assert(typeof config === 'object', '"config" must be an object');
|
|
|
|
assert(typeof config.phase === 'string' && config.phase,
|
|
|
|
'"config.phase" must be a non-empty string');
|
|
|
|
|
|
|
|
if (config.enabled === false)
|
|
|
|
return;
|
|
|
|
|
2014-11-13 12:22:25 +00:00
|
|
|
var params = config.params;
|
|
|
|
if (params === undefined) {
|
|
|
|
params = [];
|
|
|
|
} else if (!Array.isArray(params)) {
|
|
|
|
params = [params];
|
2014-11-11 16:51:50 +00:00
|
|
|
}
|
|
|
|
|
2014-11-13 12:22:25 +00:00
|
|
|
var handler = factory.apply(null, params);
|
2015-08-05 17:30:57 +00:00
|
|
|
|
|
|
|
// Check if methods/verbs filter exists
|
|
|
|
var verbs = config.methods || config.verbs;
|
|
|
|
if (Array.isArray(verbs)) {
|
|
|
|
verbs = verbs.map(function(verb) {
|
|
|
|
return verb && verb.toUpperCase();
|
|
|
|
});
|
|
|
|
if (verbs.indexOf('ALL') === -1) {
|
|
|
|
var originalHandler = handler;
|
|
|
|
if (handler.length <= 3) {
|
|
|
|
// Regular handler
|
|
|
|
handler = function(req, res, next) {
|
|
|
|
if (verbs.indexOf(req.method.toUpperCase()) === -1) {
|
|
|
|
return next();
|
|
|
|
}
|
|
|
|
originalHandler(req, res, next);
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
// Error handler
|
|
|
|
handler = function(err, req, res, next) {
|
|
|
|
if (verbs.indexOf(req.method.toUpperCase()) === -1) {
|
|
|
|
return next(err);
|
|
|
|
}
|
|
|
|
originalHandler(err, req, res, next);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
Scope app middleware to a list of paths
Add a new argument to `app.middleware` allowing developers
to restrict the middleware to a list of paths or regular expresions.
Modify `app.middlewareFromConfig` to pass `config.paths` as the second
arg of `app.middleware`.
Examples:
// A string path (interpreted via path-to-regexp)
app.middleware('auth', '/admin', ldapAuth);
// A regular expression
app.middleware('initial', /^\/~(admin|root)/, rejectWith404);
// A list of scopes
app.middleware('routes', ['/api', /^\/assets/.*\.json$/], foo);
// From config
app.middlewareFromConfig(
handlerFactory,
{
phase: 'initial',
paths: ['/scope', /^\/(a|b)/]
});
2014-11-19 11:03:47 +00:00
|
|
|
this.middleware(config.phase, config.paths || [], handler);
|
2014-11-13 12:22:25 +00:00
|
|
|
|
|
|
|
return this;
|
2014-11-11 16:51:50 +00:00
|
|
|
};
|
|
|
|
|
Implement `app.defineMiddlewarePhases`
Implement method for registering (new) middleware phases.
- If all names are new, then the phases are added just before
the "routes" phase.
- Otherwise the provided list of names is merged with the existing
phases in such way that the order of phases is preserved.
Example
// built-in phases:
// initial, session, auth, parse, routes, files, final
app.defineMiddlewarePhases('custom');
// new list of phases
// initial, session, auth, parse,
// custom,
// routes, files, final
app.defineMiddlewarePhases([
'initial', 'postinit', 'preauth', 'routes', 'subapps'
]);
// new list of phases
// initial,
// postinit, preauth,
// session, auth, parse, custom,
// routes,
// subapps,
// files, final
2014-11-11 16:13:44 +00:00
|
|
|
/**
|
|
|
|
* Register (new) middleware phases.
|
|
|
|
*
|
|
|
|
* If all names are new, then the phases are added just before "routes" phase.
|
|
|
|
* Otherwise the provided list of names is merged with the existing phases
|
|
|
|
* in such way that the order of phases is preserved.
|
|
|
|
*
|
|
|
|
* **Examples**
|
|
|
|
*
|
|
|
|
* ```js
|
|
|
|
* // built-in phases:
|
|
|
|
* // initial, session, auth, parse, routes, files, final
|
|
|
|
*
|
|
|
|
* app.defineMiddlewarePhases('custom');
|
|
|
|
* // new list of phases
|
|
|
|
* // initial, session, auth, parse, custom, routes, files, final
|
|
|
|
*
|
|
|
|
* app.defineMiddlewarePhases([
|
|
|
|
* 'initial', 'postinit', 'preauth', 'routes', 'subapps'
|
|
|
|
* ]);
|
|
|
|
* // new list of phases
|
|
|
|
* // initial, postinit, preauth, session, auth, parse, custom,
|
|
|
|
* // routes, subapps, files, final
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @param {string|Array.<string>} nameOrArray A phase name or a list of phase
|
|
|
|
* names to add.
|
2014-11-13 12:22:25 +00:00
|
|
|
*
|
|
|
|
* @returns {object} this (fluent API)
|
2014-11-13 12:30:44 +00:00
|
|
|
*
|
|
|
|
* @header app.defineMiddlewarePhases(nameOrArray)
|
Implement `app.defineMiddlewarePhases`
Implement method for registering (new) middleware phases.
- If all names are new, then the phases are added just before
the "routes" phase.
- Otherwise the provided list of names is merged with the existing
phases in such way that the order of phases is preserved.
Example
// built-in phases:
// initial, session, auth, parse, routes, files, final
app.defineMiddlewarePhases('custom');
// new list of phases
// initial, session, auth, parse,
// custom,
// routes, files, final
app.defineMiddlewarePhases([
'initial', 'postinit', 'preauth', 'routes', 'subapps'
]);
// new list of phases
// initial,
// postinit, preauth,
// session, auth, parse, custom,
// routes,
// subapps,
// files, final
2014-11-11 16:13:44 +00:00
|
|
|
*/
|
|
|
|
proto.defineMiddlewarePhases = function(nameOrArray) {
|
|
|
|
this.lazyrouter();
|
|
|
|
|
Simplify `app.defineMiddlewarePhases`
Refactor the implementation to use the new method `phaseList.zipMerge`.
This is commit is changing the behaviour in the case when
the first new phase does not exist in the current list.
Before the change, all new phases were added just before the "routes"
phase.
After this change, new phases are added to the head of the list,
until an existing phase is encountered, at which point the regular
merge algorithm kicks in.
Example:
app.defineMiddlewarePhases(['first', 'routes', 'subapps']);
Before the change: code throws an error - 'routes' already exists.
After the change: phases are merged with the following result:
'first', 'initial', ..., 'routes', 'subapps', ...
2014-11-12 07:59:56 +00:00
|
|
|
if (Array.isArray(nameOrArray)) {
|
2014-12-10 08:36:45 +00:00
|
|
|
this._requestHandlingPhases =
|
|
|
|
mergePhaseNameLists(this._requestHandlingPhases, nameOrArray);
|
Simplify `app.defineMiddlewarePhases`
Refactor the implementation to use the new method `phaseList.zipMerge`.
This is commit is changing the behaviour in the case when
the first new phase does not exist in the current list.
Before the change, all new phases were added just before the "routes"
phase.
After this change, new phases are added to the head of the list,
until an existing phase is encountered, at which point the regular
merge algorithm kicks in.
Example:
app.defineMiddlewarePhases(['first', 'routes', 'subapps']);
Before the change: code throws an error - 'routes' already exists.
After the change: phases are merged with the following result:
'first', 'initial', ..., 'routes', 'subapps', ...
2014-11-12 07:59:56 +00:00
|
|
|
} else {
|
2014-12-10 08:36:45 +00:00
|
|
|
// add the new phase before 'routes'
|
|
|
|
var routesIx = this._requestHandlingPhases.indexOf('routes');
|
|
|
|
this._requestHandlingPhases.splice(routesIx - 1, 0, nameOrArray);
|
Implement `app.defineMiddlewarePhases`
Implement method for registering (new) middleware phases.
- If all names are new, then the phases are added just before
the "routes" phase.
- Otherwise the provided list of names is merged with the existing
phases in such way that the order of phases is preserved.
Example
// built-in phases:
// initial, session, auth, parse, routes, files, final
app.defineMiddlewarePhases('custom');
// new list of phases
// initial, session, auth, parse,
// custom,
// routes, files, final
app.defineMiddlewarePhases([
'initial', 'postinit', 'preauth', 'routes', 'subapps'
]);
// new list of phases
// initial,
// postinit, preauth,
// session, auth, parse, custom,
// routes,
// subapps,
// files, final
2014-11-11 16:13:44 +00:00
|
|
|
}
|
2014-11-13 12:22:25 +00:00
|
|
|
|
|
|
|
return this;
|
Implement `app.defineMiddlewarePhases`
Implement method for registering (new) middleware phases.
- If all names are new, then the phases are added just before
the "routes" phase.
- Otherwise the provided list of names is merged with the existing
phases in such way that the order of phases is preserved.
Example
// built-in phases:
// initial, session, auth, parse, routes, files, final
app.defineMiddlewarePhases('custom');
// new list of phases
// initial, session, auth, parse,
// custom,
// routes, files, final
app.defineMiddlewarePhases([
'initial', 'postinit', 'preauth', 'routes', 'subapps'
]);
// new list of phases
// initial,
// postinit, preauth,
// session, auth, parse, custom,
// routes,
// subapps,
// files, final
2014-11-11 16:13:44 +00:00
|
|
|
};
|
|
|
|
|
Middleware phases - initial implementation
Modify the app and router implementation, so that the middleware is
executed in order defined by phases.
Predefined phases:
'initial', 'session', 'auth', 'parse', 'routes', 'files', 'final'
Methods defined via `app.use`, `app.route` and friends are executed
as the first thing in 'routes' phase.
API usage:
app.middleware('initial', compression());
app.middleware('initial:before', serveFavicon());
app.middleware('files:after', loopback.urlNotFound());
app.middleware('final:after', errorHandler());
Middleware flavours:
// regular handler
function handler(req, res, next) {
// do stuff
next();
}
// error handler
function errorHandler(err, req, res, next) {
// handle error and/or call next
next(err);
}
2014-11-05 19:07:58 +00:00
|
|
|
/**
|
|
|
|
* Register a middleware handler to be executed in a given phase.
|
|
|
|
* @param {string} name The phase name, e.g. "init" or "routes".
|
Scope app middleware to a list of paths
Add a new argument to `app.middleware` allowing developers
to restrict the middleware to a list of paths or regular expresions.
Modify `app.middlewareFromConfig` to pass `config.paths` as the second
arg of `app.middleware`.
Examples:
// A string path (interpreted via path-to-regexp)
app.middleware('auth', '/admin', ldapAuth);
// A regular expression
app.middleware('initial', /^\/~(admin|root)/, rejectWith404);
// A list of scopes
app.middleware('routes', ['/api', /^\/assets/.*\.json$/], foo);
// From config
app.middlewareFromConfig(
handlerFactory,
{
phase: 'initial',
paths: ['/scope', /^\/(a|b)/]
});
2014-11-19 11:03:47 +00:00
|
|
|
* @param {Array|string|RegExp} [paths] Optional list of paths limiting
|
|
|
|
* the scope of the middleware.
|
|
|
|
* String paths are interpreted as expressjs path patterns,
|
|
|
|
* regular expressions are used as-is.
|
Middleware phases - initial implementation
Modify the app and router implementation, so that the middleware is
executed in order defined by phases.
Predefined phases:
'initial', 'session', 'auth', 'parse', 'routes', 'files', 'final'
Methods defined via `app.use`, `app.route` and friends are executed
as the first thing in 'routes' phase.
API usage:
app.middleware('initial', compression());
app.middleware('initial:before', serveFavicon());
app.middleware('files:after', loopback.urlNotFound());
app.middleware('final:after', errorHandler());
Middleware flavours:
// regular handler
function handler(req, res, next) {
// do stuff
next();
}
// error handler
function errorHandler(err, req, res, next) {
// handle error and/or call next
next(err);
}
2014-11-05 19:07:58 +00:00
|
|
|
* @param {function} handler The middleware handler, one of
|
|
|
|
* `function(req, res, next)` or
|
|
|
|
* `function(err, req, res, next)`
|
|
|
|
* @returns {object} this (fluent API)
|
2014-11-13 12:30:44 +00:00
|
|
|
*
|
|
|
|
* @header app.middleware(name, handler)
|
Middleware phases - initial implementation
Modify the app and router implementation, so that the middleware is
executed in order defined by phases.
Predefined phases:
'initial', 'session', 'auth', 'parse', 'routes', 'files', 'final'
Methods defined via `app.use`, `app.route` and friends are executed
as the first thing in 'routes' phase.
API usage:
app.middleware('initial', compression());
app.middleware('initial:before', serveFavicon());
app.middleware('files:after', loopback.urlNotFound());
app.middleware('final:after', errorHandler());
Middleware flavours:
// regular handler
function handler(req, res, next) {
// do stuff
next();
}
// error handler
function errorHandler(err, req, res, next) {
// handle error and/or call next
next(err);
}
2014-11-05 19:07:58 +00:00
|
|
|
*/
|
Scope app middleware to a list of paths
Add a new argument to `app.middleware` allowing developers
to restrict the middleware to a list of paths or regular expresions.
Modify `app.middlewareFromConfig` to pass `config.paths` as the second
arg of `app.middleware`.
Examples:
// A string path (interpreted via path-to-regexp)
app.middleware('auth', '/admin', ldapAuth);
// A regular expression
app.middleware('initial', /^\/~(admin|root)/, rejectWith404);
// A list of scopes
app.middleware('routes', ['/api', /^\/assets/.*\.json$/], foo);
// From config
app.middlewareFromConfig(
handlerFactory,
{
phase: 'initial',
paths: ['/scope', /^\/(a|b)/]
});
2014-11-19 11:03:47 +00:00
|
|
|
proto.middleware = function(name, paths, handler) {
|
Middleware phases - initial implementation
Modify the app and router implementation, so that the middleware is
executed in order defined by phases.
Predefined phases:
'initial', 'session', 'auth', 'parse', 'routes', 'files', 'final'
Methods defined via `app.use`, `app.route` and friends are executed
as the first thing in 'routes' phase.
API usage:
app.middleware('initial', compression());
app.middleware('initial:before', serveFavicon());
app.middleware('files:after', loopback.urlNotFound());
app.middleware('final:after', errorHandler());
Middleware flavours:
// regular handler
function handler(req, res, next) {
// do stuff
next();
}
// error handler
function errorHandler(err, req, res, next) {
// handle error and/or call next
next(err);
}
2014-11-05 19:07:58 +00:00
|
|
|
this.lazyrouter();
|
|
|
|
|
Scope app middleware to a list of paths
Add a new argument to `app.middleware` allowing developers
to restrict the middleware to a list of paths or regular expresions.
Modify `app.middlewareFromConfig` to pass `config.paths` as the second
arg of `app.middleware`.
Examples:
// A string path (interpreted via path-to-regexp)
app.middleware('auth', '/admin', ldapAuth);
// A regular expression
app.middleware('initial', /^\/~(admin|root)/, rejectWith404);
// A list of scopes
app.middleware('routes', ['/api', /^\/assets/.*\.json$/], foo);
// From config
app.middlewareFromConfig(
handlerFactory,
{
phase: 'initial',
paths: ['/scope', /^\/(a|b)/]
});
2014-11-19 11:03:47 +00:00
|
|
|
if (handler === undefined && typeof paths === 'function') {
|
|
|
|
handler = paths;
|
2014-12-10 08:36:45 +00:00
|
|
|
paths = undefined;
|
Scope app middleware to a list of paths
Add a new argument to `app.middleware` allowing developers
to restrict the middleware to a list of paths or regular expresions.
Modify `app.middlewareFromConfig` to pass `config.paths` as the second
arg of `app.middleware`.
Examples:
// A string path (interpreted via path-to-regexp)
app.middleware('auth', '/admin', ldapAuth);
// A regular expression
app.middleware('initial', /^\/~(admin|root)/, rejectWith404);
// A list of scopes
app.middleware('routes', ['/api', /^\/assets/.*\.json$/], foo);
// From config
app.middlewareFromConfig(
handlerFactory,
{
phase: 'initial',
paths: ['/scope', /^\/(a|b)/]
});
2014-11-19 11:03:47 +00:00
|
|
|
}
|
|
|
|
|
2014-11-11 16:51:50 +00:00
|
|
|
assert(typeof name === 'string' && name, '"name" must be a non-empty string');
|
|
|
|
assert(typeof handler === 'function', '"handler" must be a function');
|
|
|
|
|
2014-12-10 08:36:45 +00:00
|
|
|
if (paths === undefined) {
|
|
|
|
paths = '/';
|
|
|
|
}
|
|
|
|
|
|
|
|
var fullPhaseName = name;
|
|
|
|
var handlerName = handler.name || '<anonymous>';
|
Middleware phases - initial implementation
Modify the app and router implementation, so that the middleware is
executed in order defined by phases.
Predefined phases:
'initial', 'session', 'auth', 'parse', 'routes', 'files', 'final'
Methods defined via `app.use`, `app.route` and friends are executed
as the first thing in 'routes' phase.
API usage:
app.middleware('initial', compression());
app.middleware('initial:before', serveFavicon());
app.middleware('files:after', loopback.urlNotFound());
app.middleware('final:after', errorHandler());
Middleware flavours:
// regular handler
function handler(req, res, next) {
// do stuff
next();
}
// error handler
function errorHandler(err, req, res, next) {
// handle error and/or call next
next(err);
}
2014-11-05 19:07:58 +00:00
|
|
|
|
|
|
|
var m = name.match(/^(.+):(before|after)$/);
|
|
|
|
if (m) {
|
|
|
|
name = m[1];
|
|
|
|
}
|
|
|
|
|
2014-12-10 08:36:45 +00:00
|
|
|
if (this._requestHandlingPhases.indexOf(name) === -1)
|
Middleware phases - initial implementation
Modify the app and router implementation, so that the middleware is
executed in order defined by phases.
Predefined phases:
'initial', 'session', 'auth', 'parse', 'routes', 'files', 'final'
Methods defined via `app.use`, `app.route` and friends are executed
as the first thing in 'routes' phase.
API usage:
app.middleware('initial', compression());
app.middleware('initial:before', serveFavicon());
app.middleware('files:after', loopback.urlNotFound());
app.middleware('final:after', errorHandler());
Middleware flavours:
// regular handler
function handler(req, res, next) {
// do stuff
next();
}
// error handler
function errorHandler(err, req, res, next) {
// handle error and/or call next
next(err);
}
2014-11-05 19:07:58 +00:00
|
|
|
throw new Error('Unknown middleware phase ' + name);
|
|
|
|
|
2014-12-10 08:36:45 +00:00
|
|
|
debug('use %s %s %s', fullPhaseName, paths, handlerName);
|
Scope app middleware to a list of paths
Add a new argument to `app.middleware` allowing developers
to restrict the middleware to a list of paths or regular expresions.
Modify `app.middlewareFromConfig` to pass `config.paths` as the second
arg of `app.middleware`.
Examples:
// A string path (interpreted via path-to-regexp)
app.middleware('auth', '/admin', ldapAuth);
// A regular expression
app.middleware('initial', /^\/~(admin|root)/, rejectWith404);
// A list of scopes
app.middleware('routes', ['/api', /^\/assets/.*\.json$/], foo);
// From config
app.middlewareFromConfig(
handlerFactory,
{
phase: 'initial',
paths: ['/scope', /^\/(a|b)/]
});
2014-11-19 11:03:47 +00:00
|
|
|
|
2014-12-10 08:36:45 +00:00
|
|
|
this._skipLayerSorting = true;
|
|
|
|
this.use(paths, handler);
|
2015-02-25 17:47:18 +00:00
|
|
|
|
|
|
|
var layer = this._findLayerByHandler(handler);
|
|
|
|
if (layer) {
|
|
|
|
// Set the phase name for sorting
|
|
|
|
layer.phase = fullPhaseName;
|
|
|
|
} else {
|
|
|
|
debug('No matching layer is found for %s %s', fullPhaseName, handlerName);
|
|
|
|
}
|
|
|
|
|
2014-12-10 08:36:45 +00:00
|
|
|
this._skipLayerSorting = false;
|
Scope app middleware to a list of paths
Add a new argument to `app.middleware` allowing developers
to restrict the middleware to a list of paths or regular expresions.
Modify `app.middlewareFromConfig` to pass `config.paths` as the second
arg of `app.middleware`.
Examples:
// A string path (interpreted via path-to-regexp)
app.middleware('auth', '/admin', ldapAuth);
// A regular expression
app.middleware('initial', /^\/~(admin|root)/, rejectWith404);
// A list of scopes
app.middleware('routes', ['/api', /^\/assets/.*\.json$/], foo);
// From config
app.middlewareFromConfig(
handlerFactory,
{
phase: 'initial',
paths: ['/scope', /^\/(a|b)/]
});
2014-11-19 11:03:47 +00:00
|
|
|
|
2014-12-10 08:36:45 +00:00
|
|
|
this._sortLayersByPhase();
|
Scope app middleware to a list of paths
Add a new argument to `app.middleware` allowing developers
to restrict the middleware to a list of paths or regular expresions.
Modify `app.middlewareFromConfig` to pass `config.paths` as the second
arg of `app.middleware`.
Examples:
// A string path (interpreted via path-to-regexp)
app.middleware('auth', '/admin', ldapAuth);
// A regular expression
app.middleware('initial', /^\/~(admin|root)/, rejectWith404);
// A list of scopes
app.middleware('routes', ['/api', /^\/assets/.*\.json$/], foo);
// From config
app.middlewareFromConfig(
handlerFactory,
{
phase: 'initial',
paths: ['/scope', /^\/(a|b)/]
});
2014-11-19 11:03:47 +00:00
|
|
|
|
2014-12-10 08:36:45 +00:00
|
|
|
return this;
|
|
|
|
};
|
Middleware phases - initial implementation
Modify the app and router implementation, so that the middleware is
executed in order defined by phases.
Predefined phases:
'initial', 'session', 'auth', 'parse', 'routes', 'files', 'final'
Methods defined via `app.use`, `app.route` and friends are executed
as the first thing in 'routes' phase.
API usage:
app.middleware('initial', compression());
app.middleware('initial:before', serveFavicon());
app.middleware('files:after', loopback.urlNotFound());
app.middleware('final:after', errorHandler());
Middleware flavours:
// regular handler
function handler(req, res, next) {
// do stuff
next();
}
// error handler
function errorHandler(err, req, res, next) {
// handle error and/or call next
next(err);
}
2014-11-05 19:07:58 +00:00
|
|
|
|
2015-05-04 19:23:21 +00:00
|
|
|
/*!
|
2015-02-25 17:47:18 +00:00
|
|
|
* Find the corresponding express layer by handler
|
|
|
|
*
|
|
|
|
* This is needed because monitoring agents such as NewRelic can add handlers
|
|
|
|
* to the stack. For example, NewRelic adds sentinel handler. We need to search
|
|
|
|
* the stackto find the correct layer.
|
|
|
|
*/
|
|
|
|
proto._findLayerByHandler = function(handler) {
|
|
|
|
// Other handlers can be added to the stack, for example,
|
|
|
|
// NewRelic adds sentinel handler. We need to search the stack
|
|
|
|
for (var k = this._router.stack.length - 1; k >= 0; k--) {
|
|
|
|
if (this._router.stack[k].handle === handler ||
|
|
|
|
// NewRelic replaces the handle and keeps it as __NR_original
|
|
|
|
this._router.stack[k].handle['__NR_original'] === handler
|
|
|
|
) {
|
|
|
|
return this._router.stack[k];
|
|
|
|
} else {
|
|
|
|
// Aggressively check if the original handler has been wrapped
|
|
|
|
// into a new function with a property pointing to the original handler
|
|
|
|
for (var p in this._router.stack[k].handle) {
|
|
|
|
if (this._router.stack[k].handle[p] === handler) {
|
|
|
|
return this._router.stack[k];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
};
|
|
|
|
|
Middleware phases - initial implementation
Modify the app and router implementation, so that the middleware is
executed in order defined by phases.
Predefined phases:
'initial', 'session', 'auth', 'parse', 'routes', 'files', 'final'
Methods defined via `app.use`, `app.route` and friends are executed
as the first thing in 'routes' phase.
API usage:
app.middleware('initial', compression());
app.middleware('initial:before', serveFavicon());
app.middleware('files:after', loopback.urlNotFound());
app.middleware('final:after', errorHandler());
Middleware flavours:
// regular handler
function handler(req, res, next) {
// do stuff
next();
}
// error handler
function errorHandler(err, req, res, next) {
// handle error and/or call next
next(err);
}
2014-11-05 19:07:58 +00:00
|
|
|
// Install our custom PhaseList-based handler into the app
|
|
|
|
proto.lazyrouter = function() {
|
|
|
|
var self = this;
|
|
|
|
if (self._router) return;
|
|
|
|
|
|
|
|
self.__expressLazyRouter();
|
|
|
|
|
2014-12-10 08:36:45 +00:00
|
|
|
var router = self._router;
|
Middleware phases - initial implementation
Modify the app and router implementation, so that the middleware is
executed in order defined by phases.
Predefined phases:
'initial', 'session', 'auth', 'parse', 'routes', 'files', 'final'
Methods defined via `app.use`, `app.route` and friends are executed
as the first thing in 'routes' phase.
API usage:
app.middleware('initial', compression());
app.middleware('initial:before', serveFavicon());
app.middleware('files:after', loopback.urlNotFound());
app.middleware('final:after', errorHandler());
Middleware flavours:
// regular handler
function handler(req, res, next) {
// do stuff
next();
}
// error handler
function errorHandler(err, req, res, next) {
// handle error and/or call next
next(err);
}
2014-11-05 19:07:58 +00:00
|
|
|
|
2014-12-10 08:36:45 +00:00
|
|
|
// Mark all middleware added by Router ctor as builtin
|
|
|
|
// The sorting algo will keep them at beginning of the list
|
|
|
|
router.stack.forEach(function(layer) {
|
|
|
|
layer.phase = BUILTIN_MIDDLEWARE;
|
|
|
|
});
|
|
|
|
|
|
|
|
router.__expressUse = router.use;
|
|
|
|
router.use = function useAndSort() {
|
|
|
|
var retval = this.__expressUse.apply(this, arguments);
|
|
|
|
self._sortLayersByPhase();
|
|
|
|
return retval;
|
|
|
|
};
|
|
|
|
|
|
|
|
router.__expressRoute = router.route;
|
|
|
|
router.route = function routeAndSort() {
|
|
|
|
var retval = this.__expressRoute.apply(this, arguments);
|
|
|
|
self._sortLayersByPhase();
|
|
|
|
return retval;
|
|
|
|
};
|
|
|
|
|
|
|
|
self._requestHandlingPhases = [
|
Middleware phases - initial implementation
Modify the app and router implementation, so that the middleware is
executed in order defined by phases.
Predefined phases:
'initial', 'session', 'auth', 'parse', 'routes', 'files', 'final'
Methods defined via `app.use`, `app.route` and friends are executed
as the first thing in 'routes' phase.
API usage:
app.middleware('initial', compression());
app.middleware('initial:before', serveFavicon());
app.middleware('files:after', loopback.urlNotFound());
app.middleware('final:after', errorHandler());
Middleware flavours:
// regular handler
function handler(req, res, next) {
// do stuff
next();
}
// error handler
function errorHandler(err, req, res, next) {
// handle error and/or call next
next(err);
}
2014-11-05 19:07:58 +00:00
|
|
|
'initial', 'session', 'auth', 'parse',
|
2016-04-01 09:14:26 +00:00
|
|
|
'routes', 'files', 'final',
|
2014-12-10 08:36:45 +00:00
|
|
|
];
|
|
|
|
};
|
Middleware phases - initial implementation
Modify the app and router implementation, so that the middleware is
executed in order defined by phases.
Predefined phases:
'initial', 'session', 'auth', 'parse', 'routes', 'files', 'final'
Methods defined via `app.use`, `app.route` and friends are executed
as the first thing in 'routes' phase.
API usage:
app.middleware('initial', compression());
app.middleware('initial:before', serveFavicon());
app.middleware('files:after', loopback.urlNotFound());
app.middleware('final:after', errorHandler());
Middleware flavours:
// regular handler
function handler(req, res, next) {
// do stuff
next();
}
// error handler
function errorHandler(err, req, res, next) {
// handle error and/or call next
next(err);
}
2014-11-05 19:07:58 +00:00
|
|
|
|
2014-12-10 08:36:45 +00:00
|
|
|
proto._sortLayersByPhase = function() {
|
|
|
|
if (this._skipLayerSorting) return;
|
Middleware phases - initial implementation
Modify the app and router implementation, so that the middleware is
executed in order defined by phases.
Predefined phases:
'initial', 'session', 'auth', 'parse', 'routes', 'files', 'final'
Methods defined via `app.use`, `app.route` and friends are executed
as the first thing in 'routes' phase.
API usage:
app.middleware('initial', compression());
app.middleware('initial:before', serveFavicon());
app.middleware('files:after', loopback.urlNotFound());
app.middleware('final:after', errorHandler());
Middleware flavours:
// regular handler
function handler(req, res, next) {
// do stuff
next();
}
// error handler
function errorHandler(err, req, res, next) {
// handle error and/or call next
next(err);
}
2014-11-05 19:07:58 +00:00
|
|
|
|
2014-12-10 08:36:45 +00:00
|
|
|
var phaseOrder = {};
|
|
|
|
this._requestHandlingPhases.forEach(function(name, ix) {
|
|
|
|
phaseOrder[name + ':before'] = ix * 3;
|
|
|
|
phaseOrder[name] = ix * 3 + 1;
|
|
|
|
phaseOrder[name + ':after'] = ix * 3 + 2;
|
Middleware phases - initial implementation
Modify the app and router implementation, so that the middleware is
executed in order defined by phases.
Predefined phases:
'initial', 'session', 'auth', 'parse', 'routes', 'files', 'final'
Methods defined via `app.use`, `app.route` and friends are executed
as the first thing in 'routes' phase.
API usage:
app.middleware('initial', compression());
app.middleware('initial:before', serveFavicon());
app.middleware('files:after', loopback.urlNotFound());
app.middleware('final:after', errorHandler());
Middleware flavours:
// regular handler
function handler(req, res, next) {
// do stuff
next();
}
// error handler
function errorHandler(err, req, res, next) {
// handle error and/or call next
next(err);
}
2014-11-05 19:07:58 +00:00
|
|
|
});
|
|
|
|
|
2014-12-10 08:36:45 +00:00
|
|
|
var router = this._router;
|
2014-12-15 07:14:26 +00:00
|
|
|
stableSortInPlace(router.stack, compareLayers);
|
2014-12-10 08:36:45 +00:00
|
|
|
|
|
|
|
function compareLayers(left, right) {
|
|
|
|
var leftPhase = left.phase;
|
|
|
|
var rightPhase = right.phase;
|
|
|
|
|
|
|
|
if (leftPhase === rightPhase) return 0;
|
|
|
|
|
|
|
|
// Builtin middleware is always first
|
|
|
|
if (leftPhase === BUILTIN_MIDDLEWARE) return -1;
|
|
|
|
if (rightPhase === BUILTIN_MIDDLEWARE) return 1;
|
|
|
|
|
|
|
|
// Layers registered via app.use and app.route
|
|
|
|
// are executed as the first items in `routes` phase
|
|
|
|
if (leftPhase === undefined) {
|
|
|
|
if (rightPhase === 'routes')
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return phaseOrder['routes'] - phaseOrder[rightPhase];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rightPhase === undefined)
|
|
|
|
return -compareLayers(right, left);
|
|
|
|
|
|
|
|
// Layers registered via `app.middleware` are compared via phase & hook
|
|
|
|
return phaseOrder[leftPhase] - phaseOrder[rightPhase];
|
|
|
|
}
|
Middleware phases - initial implementation
Modify the app and router implementation, so that the middleware is
executed in order defined by phases.
Predefined phases:
'initial', 'session', 'auth', 'parse', 'routes', 'files', 'final'
Methods defined via `app.use`, `app.route` and friends are executed
as the first thing in 'routes' phase.
API usage:
app.middleware('initial', compression());
app.middleware('initial:before', serveFavicon());
app.middleware('files:after', loopback.urlNotFound());
app.middleware('final:after', errorHandler());
Middleware flavours:
// regular handler
function handler(req, res, next) {
// do stuff
next();
}
// error handler
function errorHandler(err, req, res, next) {
// handle error and/or call next
next(err);
}
2014-11-05 19:07:58 +00:00
|
|
|
};
|