'use strict';
|
|
const join = require('path').join;
|
const is = require('is-type-of');
|
const inspect = require('util').inspect;
|
const assert = require('assert');
|
const debug = require('debug')('egg-core:middleware');
|
const pathMatching = require('egg-path-matching');
|
const utils = require('../../utils');
|
|
|
module.exports = {
|
|
/**
|
* Load app/middleware
|
*
|
* app.config.xx is the options of the middleware xx that has same name as config
|
*
|
* @method EggLoader#loadMiddleware
|
* @param {Object} opt - LoaderOptions
|
* @example
|
* ```js
|
* // app/middleware/status.js
|
* module.exports = function(options, app) {
|
* // options == app.config.status
|
* return function*(next) {
|
* yield next;
|
* }
|
* }
|
* ```
|
* @since 1.0.0
|
*/
|
loadMiddleware(opt) {
|
this.timing.start('Load Middleware');
|
const app = this.app;
|
|
// load middleware to app.middleware
|
opt = Object.assign({
|
call: false,
|
override: true,
|
caseStyle: 'lower',
|
directory: this.getLoadUnits().map(unit => join(unit.path, 'app/middleware')),
|
}, opt);
|
const middlewarePaths = opt.directory;
|
this.loadToApp(middlewarePaths, 'middlewares', opt);
|
|
for (const name in app.middlewares) {
|
Object.defineProperty(app.middleware, name, {
|
get() {
|
return app.middlewares[name];
|
},
|
enumerable: false,
|
configurable: false,
|
});
|
}
|
|
this.options.logger.info('Use coreMiddleware order: %j', this.config.coreMiddleware);
|
this.options.logger.info('Use appMiddleware order: %j', this.config.appMiddleware);
|
|
// use middleware ordered by app.config.coreMiddleware and app.config.appMiddleware
|
const middlewareNames = this.config.coreMiddleware.concat(this.config.appMiddleware);
|
debug('middlewareNames: %j', middlewareNames);
|
const middlewaresMap = new Map();
|
for (const name of middlewareNames) {
|
if (!app.middlewares[name]) {
|
throw new TypeError(`Middleware ${name} not found`);
|
}
|
if (middlewaresMap.has(name)) {
|
throw new TypeError(`Middleware ${name} redefined`);
|
}
|
middlewaresMap.set(name, true);
|
|
const options = this.config[name] || {};
|
let mw = app.middlewares[name];
|
mw = mw(options, app);
|
assert(is.function(mw), `Middleware ${name} must be a function, but actual is ${inspect(mw)}`);
|
mw._name = name;
|
// middlewares support options.enable, options.ignore and options.match
|
mw = wrapMiddleware(mw, options);
|
if (mw) {
|
if (debug.enabled) {
|
// show mw debug log on every request
|
mw = debugWrapper(mw);
|
}
|
app.use(mw);
|
debug('Use middleware: %s with options: %j', name, options);
|
this.options.logger.info('[egg:loader] Use middleware: %s', name);
|
} else {
|
this.options.logger.info('[egg:loader] Disable middleware: %s', name);
|
}
|
}
|
|
this.options.logger.info('[egg:loader] Loaded middleware from %j', middlewarePaths);
|
this.timing.end('Load Middleware');
|
},
|
|
};
|
|
function wrapMiddleware(mw, options) {
|
// support options.enable
|
if (options.enable === false) return null;
|
|
// support generator function
|
mw = utils.middleware(mw);
|
|
// support options.match and options.ignore
|
if (!options.match && !options.ignore) return mw;
|
const match = pathMatching(options);
|
|
const fn = (ctx, next) => {
|
if (!match(ctx)) return next();
|
return mw(ctx, next);
|
};
|
fn._name = mw._name + 'middlewareWrapper';
|
return fn;
|
}
|
|
function debugWrapper(mw) {
|
const fn = (ctx, next) => {
|
debug('[%s %s] enter middleware: %s', ctx.method, ctx.url, mw._name);
|
return mw(ctx, next);
|
};
|
fn._name = mw._name + 'DebugWrapper';
|
return fn;
|
}
|