2018-01-03 04:05:53 +00:00
|
|
|
// Copyright IBM Corp. 2014,2018. All Rights Reserved.
|
2016-05-03 22:50:21 +00:00
|
|
|
// Node module: loopback
|
|
|
|
// This file is licensed under the MIT License.
|
|
|
|
// License text available at https://opensource.org/licenses/MIT
|
|
|
|
|
2016-11-15 21:46:23 +00:00
|
|
|
'use strict';
|
2019-10-07 09:45:34 +00:00
|
|
|
const g = require('./globalize');
|
|
|
|
const assert = require('assert');
|
|
|
|
const express = require('express');
|
|
|
|
const merge = require('util')._extend;
|
|
|
|
const mergePhaseNameLists = require('loopback-phase').mergePhaseNameLists;
|
|
|
|
const debug = require('debug')('loopback:app');
|
|
|
|
const stableSortInPlace = require('stable').inplace;
|
2014-12-10 08:36:45 +00:00
|
|
|
|
2019-10-07 09:45:34 +00:00
|
|
|
const 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
|
|
|
|
2019-10-07 09:45:34 +00:00
|
|
|
const proto = {};
|
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
|
|
|
|
|
|
|
module.exports = function loopbackExpress() {
|
2019-10-07 09:45:34 +00:00
|
|
|
const app = express();
|
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
|
|
|
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;
|
|
|
|
|
2019-10-07 09:45:34 +00:00
|
|
|
let params = config.params;
|
2014-11-13 12:22:25 +00:00
|
|
|
if (params === undefined) {
|
|
|
|
params = [];
|
|
|
|
} else if (!Array.isArray(params)) {
|
|
|
|
params = [params];
|
2014-11-11 16:51:50 +00:00
|
|
|
}
|
|
|
|
|
2019-10-07 09:45:34 +00:00
|
|
|
let handler = factory.apply(null, params);
|
2015-08-05 17:30:57 +00:00
|
|
|
|
|
|
|
// Check if methods/verbs filter exists
|
2019-10-07 09:45:34 +00:00
|
|
|
let verbs = config.methods || config.verbs;
|
2015-08-05 17:30:57 +00:00
|
|
|
if (Array.isArray(verbs)) {
|
|
|
|
verbs = verbs.map(function(verb) {
|
|
|
|
return verb && verb.toUpperCase();
|
|
|
|
});
|
|
|
|
if (verbs.indexOf('ALL') === -1) {
|
2019-10-07 09:45:34 +00:00
|
|
|
const originalHandler = handler;
|
2015-08-05 17:30:57 +00:00
|
|
|
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'
|
2019-10-07 09:45:34 +00:00
|
|
|
const routesIx = this._requestHandlingPhases.indexOf('routes');
|
2014-12-10 08:36:45 +00:00
|
|
|
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 = '/';
|
|
|
|
}
|
|
|
|
|
2019-10-07 09:45:34 +00:00
|
|
|
const fullPhaseName = name;
|
|
|
|
const 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
|
|
|
|
2019-10-07 09:45:34 +00:00
|
|
|
const m = name.match(/^(.+):(before|after)$/);
|
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
|
|
|
if (m) {
|
|
|
|
name = m[1];
|
|
|
|
}
|
|
|
|
|
2014-12-10 08:36:45 +00:00
|
|
|
if (this._requestHandlingPhases.indexOf(name) === -1)
|
2016-06-07 14:48:28 +00:00
|
|
|
throw new Error(g.f('Unknown {{middleware}} phase %s', name));
|
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
|
|
|
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
|
|
|
|
2019-10-07 09:45:34 +00:00
|
|
|
const layer = this._findLayerByHandler(handler);
|
2015-02-25 17:47:18 +00:00
|
|
|
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,
|
2018-02-12 19:28:19 +00:00
|
|
|
// NewRelic adds sentinel handler, and AppDynamics adds
|
|
|
|
// some additional proxy info. We need to search the stack
|
2019-10-07 09:45:34 +00:00
|
|
|
for (let k = this._router.stack.length - 1; k >= 0; k--) {
|
2018-02-12 19:28:19 +00:00
|
|
|
const isOriginal = this._router.stack[k].handle === handler;
|
|
|
|
const isNewRelic = this._router.stack[k].handle['__NR_original'] === handler;
|
|
|
|
const isAppDynamics = this._router.stack[k].handle['__appdynamicsProxyInfo__'] &&
|
|
|
|
this._router.stack[k].handle['__appdynamicsProxyInfo__']['orig'] === handler;
|
|
|
|
|
|
|
|
if (isOriginal || isNewRelic || isAppDynamics) {
|
2015-02-25 17:47:18 +00:00
|
|
|
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
|
2019-10-07 09:45:34 +00:00
|
|
|
for (const p in this._router.stack[k].handle) {
|
2015-02-25 17:47:18 +00:00
|
|
|
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() {
|
2019-10-07 09:45:34 +00:00
|
|
|
const self = 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
|
|
|
if (self._router) return;
|
|
|
|
|
|
|
|
self.__expressLazyRouter();
|
|
|
|
|
2019-10-07 09:45:34 +00:00
|
|
|
const 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() {
|
2019-10-07 09:45:34 +00:00
|
|
|
const retval = this.__expressUse.apply(this, arguments);
|
2014-12-10 08:36:45 +00:00
|
|
|
self._sortLayersByPhase();
|
|
|
|
return retval;
|
|
|
|
};
|
|
|
|
|
|
|
|
router.__expressRoute = router.route;
|
|
|
|
router.route = function routeAndSort() {
|
2019-10-07 09:45:34 +00:00
|
|
|
const retval = this.__expressRoute.apply(this, arguments);
|
2014-12-10 08:36:45 +00:00
|
|
|
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
|
|
|
|
2019-10-07 09:45:34 +00:00
|
|
|
const phaseOrder = {};
|
2014-12-10 08:36:45 +00:00
|
|
|
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
|
|
|
});
|
|
|
|
|
2019-10-07 09:45:34 +00:00
|
|
|
const 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) {
|
2019-10-07 09:45:34 +00:00
|
|
|
const leftPhase = left.phase;
|
|
|
|
const rightPhase = right.phase;
|
2014-12-10 08:36:45 +00:00
|
|
|
|
|
|
|
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
|
|
|
};
|