middleware.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. 'use strict';
  2. const join = require('path').join;
  3. const is = require('is-type-of');
  4. const inspect = require('util').inspect;
  5. const assert = require('assert');
  6. const debug = require('debug')('egg-core:middleware');
  7. const pathMatching = require('egg-path-matching');
  8. const utils = require('../../utils');
  9. module.exports = {
  10. /**
  11. * Load app/middleware
  12. *
  13. * app.config.xx is the options of the middleware xx that has same name as config
  14. *
  15. * @function EggLoader#loadMiddleware
  16. * @param {Object} opt - LoaderOptions
  17. * @example
  18. * ```js
  19. * // app/middleware/status.js
  20. * module.exports = function(options, app) {
  21. * // options == app.config.status
  22. * return function*(next) {
  23. * yield next;
  24. * }
  25. * }
  26. * ```
  27. * @since 1.0.0
  28. */
  29. loadMiddleware(opt) {
  30. this.timing.start('Load Middleware');
  31. const app = this.app;
  32. // load middleware to app.middleware
  33. opt = Object.assign({
  34. call: false,
  35. override: true,
  36. caseStyle: 'lower',
  37. directory: this.getLoadUnits().map(unit => join(unit.path, 'app/middleware')),
  38. }, opt);
  39. const middlewarePaths = opt.directory;
  40. this.loadToApp(middlewarePaths, 'middlewares', opt);
  41. for (const name in app.middlewares) {
  42. Object.defineProperty(app.middleware, name, {
  43. get() {
  44. return app.middlewares[name];
  45. },
  46. enumerable: false,
  47. configurable: false,
  48. });
  49. }
  50. this.options.logger.info('Use coreMiddleware order: %j', this.config.coreMiddleware);
  51. this.options.logger.info('Use appMiddleware order: %j', this.config.appMiddleware);
  52. // use middleware ordered by app.config.coreMiddleware and app.config.appMiddleware
  53. const middlewareNames = this.config.coreMiddleware.concat(this.config.appMiddleware);
  54. debug('middlewareNames: %j', middlewareNames);
  55. const middlewaresMap = new Map();
  56. for (const name of middlewareNames) {
  57. if (!app.middlewares[name]) {
  58. throw new TypeError(`Middleware ${name} not found`);
  59. }
  60. if (middlewaresMap.has(name)) {
  61. throw new TypeError(`Middleware ${name} redefined`);
  62. }
  63. middlewaresMap.set(name, true);
  64. const options = this.config[name] || {};
  65. let mw = app.middlewares[name];
  66. mw = mw(options, app);
  67. assert(is.function(mw), `Middleware ${name} must be a function, but actual is ${inspect(mw)}`);
  68. mw._name = name;
  69. // middlewares support options.enable, options.ignore and options.match
  70. mw = wrapMiddleware(mw, options);
  71. if (mw) {
  72. if (debug.enabled) {
  73. // show mw debug log on every request
  74. mw = debugWrapper(mw);
  75. }
  76. app.use(mw);
  77. debug('Use middleware: %s with options: %j', name, options);
  78. this.options.logger.info('[egg:loader] Use middleware: %s', name);
  79. } else {
  80. this.options.logger.info('[egg:loader] Disable middleware: %s', name);
  81. }
  82. }
  83. this.options.logger.info('[egg:loader] Loaded middleware from %j', middlewarePaths);
  84. this.timing.end('Load Middleware');
  85. },
  86. };
  87. function wrapMiddleware(mw, options) {
  88. // support options.enable
  89. if (options.enable === false) return null;
  90. // support generator function
  91. mw = utils.middleware(mw);
  92. // support options.match and options.ignore
  93. if (!options.match && !options.ignore) return mw;
  94. const match = pathMatching(options);
  95. const fn = (ctx, next) => {
  96. if (!match(ctx)) return next();
  97. return mw(ctx, next);
  98. };
  99. fn._name = mw._name + 'middlewareWrapper';
  100. return fn;
  101. }
  102. function debugWrapper(mw) {
  103. const fn = (ctx, next) => {
  104. debug('[%s %s] enter middleware: %s', ctx.method, ctx.url, mw._name);
  105. return mw(ctx, next);
  106. };
  107. fn._name = mw._name + 'DebugWrapper';
  108. return fn;
  109. }