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;
|
|
|
|
var PhaseList = require('loopback-phase').PhaseList;
|
|
|
|
var debug = require('debug')('loopback:app');
|
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
|
|
|
var pathToRegexp = require('path-to-regexp');
|
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);
|
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)) {
|
|
|
|
this._requestHandlingPhases.zipMerge(nameOrArray);
|
|
|
|
} else {
|
|
|
|
this._requestHandlingPhases.addBefore('routes', 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;
|
|
|
|
paths = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof paths === 'string' || paths instanceof RegExp) {
|
|
|
|
paths = [paths];
|
|
|
|
}
|
|
|
|
|
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');
|
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
|
|
|
assert(Array.isArray(paths), '"paths" must be an array');
|
2014-11-11 16:51:50 +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
|
|
|
var fullName = name;
|
|
|
|
var handlerName = handler.name || '(anonymous)';
|
|
|
|
|
|
|
|
var hook = 'use';
|
|
|
|
var m = name.match(/^(.+):(before|after)$/);
|
|
|
|
if (m) {
|
|
|
|
name = m[1];
|
|
|
|
hook = m[2];
|
|
|
|
}
|
|
|
|
|
|
|
|
var phase = this._requestHandlingPhases.find(name);
|
|
|
|
if (!phase)
|
|
|
|
throw new Error('Unknown middleware phase ' + name);
|
|
|
|
|
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
|
|
|
var matches = createRequestMatcher(paths);
|
|
|
|
|
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 wrapper;
|
|
|
|
if (handler.length === 4) {
|
|
|
|
// handler is function(err, req, res, next)
|
|
|
|
debug('Add error handler %j to phase %j', handlerName, fullName);
|
|
|
|
|
|
|
|
wrapper = function errorHandler(ctx, 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
|
|
|
if (ctx.err && matches(ctx.req)) {
|
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 err = ctx.err;
|
|
|
|
ctx.err = undefined;
|
|
|
|
handler(err, ctx.req, ctx.res, storeErrorAndContinue(ctx, next));
|
|
|
|
} else {
|
|
|
|
next();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
// handler is function(req, res, next)
|
|
|
|
debug('Add middleware %j to phase %j', handlerName , fullName);
|
|
|
|
wrapper = function regularHandler(ctx, 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
|
|
|
if (ctx.err || !matches(ctx.req)) {
|
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
|
|
|
next();
|
|
|
|
} else {
|
|
|
|
handler(ctx.req, ctx.res, storeErrorAndContinue(ctx, next));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
phase[hook](wrapper);
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
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
|
|
|
function createRequestMatcher(paths) {
|
|
|
|
if (!paths.length) {
|
|
|
|
return function requestMatcher(req) { return true; };
|
|
|
|
}
|
|
|
|
|
|
|
|
var checks = paths.map(function(p) {
|
|
|
|
return pathToRegexp(p, {
|
|
|
|
sensitive: true,
|
|
|
|
strict: false,
|
|
|
|
end: false
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
return function requestMatcher(req) {
|
|
|
|
return checks.some(function(regex) {
|
|
|
|
return regex.test(req.url);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
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
|
|
|
function storeErrorAndContinue(ctx, next) {
|
|
|
|
return function(err) {
|
|
|
|
if (err) ctx.err = err;
|
|
|
|
next();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Install our custom PhaseList-based handler into the app
|
|
|
|
proto.lazyrouter = function() {
|
|
|
|
var self = this;
|
|
|
|
if (self._router) return;
|
|
|
|
|
|
|
|
self.__expressLazyRouter();
|
|
|
|
|
|
|
|
// Storing the fn in another property of the router object
|
|
|
|
// allows us to call the method with the router as `this`
|
|
|
|
// without the need to use slow `call` or `apply`.
|
|
|
|
self._router.__expressHandle = self._router.handle;
|
|
|
|
|
|
|
|
self._requestHandlingPhases = new PhaseList();
|
|
|
|
self._requestHandlingPhases.add([
|
|
|
|
'initial', 'session', 'auth', 'parse',
|
|
|
|
'routes', 'files', 'final'
|
|
|
|
]);
|
|
|
|
|
|
|
|
// In order to pass error into express router, we have
|
|
|
|
// to pass it to a middleware executed from within the router.
|
|
|
|
// This is achieved by adding a phase-handler that wraps the error
|
|
|
|
// into `req` object and then a router-handler that unwraps the error
|
|
|
|
// and calls `next(err)`.
|
|
|
|
// It is important to register these two handlers at the very beginning,
|
|
|
|
// before any other handlers are added.
|
|
|
|
self.middleware('routes', function wrapError(err, req, res, next) {
|
|
|
|
req.__err = err;
|
|
|
|
next();
|
|
|
|
});
|
|
|
|
|
|
|
|
self.use(function unwrapError(req, res, next) {
|
|
|
|
var err = req.__err;
|
|
|
|
req.__err = undefined;
|
|
|
|
next(err);
|
|
|
|
});
|
|
|
|
|
|
|
|
self.middleware('routes', function runRootHandlers(req, res, next) {
|
|
|
|
self._router.__expressHandle(req, res, next);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Overwrite the original handle() function provided by express,
|
|
|
|
// replace it with our implementation based on PhaseList
|
|
|
|
self._router.handle = function(req, res, next) {
|
|
|
|
var ctx = { req: req, res: res };
|
|
|
|
self._requestHandlingPhases.run(ctx, function(err) {
|
|
|
|
next(err || ctx.err);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
};
|