route.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. import {Debuggable} from '@e22m4u/js-debug';
  2. import {HookRegistry, RouterHookType} from '../hooks/index.js';
  3. import {MODULE_DEBUG_NAMESPACE} from '../debuggable-service.js';
  4. import {cloneDeep, getRequestPathname} from '../utils/index.js';
  5. import {validateRouteDefinition} from './validate-route-definition.js';
  6. /**
  7. * @typedef {import('./request-context.js').RequestContext} RequestContext
  8. * @typedef {(ctx: RequestContext) => *} RoutePreHandler
  9. * @typedef {(ctx: RequestContext) => *} RouteHandler
  10. * @typedef {(ctx: RequestContext, data: *) => *} RoutePostHandler
  11. * @typedef {{
  12. * method: string,
  13. * path: string,
  14. * handler: RouteHandler,
  15. * preHandler?: RoutePreHandler|(RoutePreHandler[]),
  16. * postHandler?: RoutePostHandler|(RoutePostHandler[]),
  17. * meta?: object,
  18. * }} RouteDefinition
  19. */
  20. /**
  21. * Http method.
  22. *
  23. * @type {{
  24. * GET: 'GET',
  25. * POST: 'POST',
  26. * PUT: 'PUT',
  27. * PATCH: 'PATCH',
  28. * DELETE: 'DELETE',
  29. * }}
  30. */
  31. export const HttpMethod = {
  32. GET: 'GET',
  33. POST: 'POST',
  34. PUT: 'PUT',
  35. PATCH: 'PATCH',
  36. DELETE: 'DELETE',
  37. };
  38. /**
  39. * Route.
  40. */
  41. export class Route extends Debuggable {
  42. /**
  43. * Method.
  44. *
  45. * @type {string}
  46. * @private
  47. */
  48. _method;
  49. /**
  50. * Getter of the method.
  51. *
  52. * @returns {string}
  53. */
  54. get method() {
  55. return this._method;
  56. }
  57. /**
  58. * Path template.
  59. *
  60. * @type {string}
  61. * @private
  62. */
  63. _path;
  64. /**
  65. * Getter of the path.
  66. *
  67. * @returns {string}
  68. */
  69. get path() {
  70. return this._path;
  71. }
  72. /**
  73. * Meta.
  74. *
  75. * @type {object}
  76. */
  77. _meta = {};
  78. /**
  79. * Getter of the meta.
  80. *
  81. * @returns {object}
  82. */
  83. get meta() {
  84. return this._meta;
  85. }
  86. /**
  87. * Handler.
  88. *
  89. * @type {RouteHandler}
  90. * @private
  91. */
  92. _handler;
  93. /**
  94. * Getter of the handler.
  95. *
  96. * @returns {*}
  97. */
  98. get handler() {
  99. return this._handler;
  100. }
  101. /**
  102. * Hook registry.
  103. *
  104. * @type {HookRegistry}
  105. * @private
  106. */
  107. _hookRegistry = new HookRegistry();
  108. /**
  109. * Getter of the hook registry.
  110. *
  111. * @returns {HookRegistry}
  112. */
  113. get hookRegistry() {
  114. return this._hookRegistry;
  115. }
  116. /**
  117. * Constructor.
  118. *
  119. * @param {RouteDefinition} routeDef
  120. */
  121. constructor(routeDef) {
  122. super({
  123. namespace: MODULE_DEBUG_NAMESPACE,
  124. noEnvironmentNamespace: true,
  125. noInstantiationMessage: true,
  126. });
  127. validateRouteDefinition(routeDef);
  128. this._method = routeDef.method.toUpperCase();
  129. this._path = routeDef.path;
  130. if (routeDef.meta !== undefined) {
  131. this._meta = cloneDeep(routeDef.meta);
  132. }
  133. this._handler = routeDef.handler;
  134. if (routeDef.preHandler !== undefined) {
  135. const preHandlerHooks = Array.isArray(routeDef.preHandler)
  136. ? routeDef.preHandler
  137. : [routeDef.preHandler];
  138. preHandlerHooks.forEach(hook => {
  139. this._hookRegistry.addHook(RouterHookType.PRE_HANDLER, hook);
  140. });
  141. }
  142. if (routeDef.postHandler !== undefined) {
  143. const postHandlerHooks = Array.isArray(routeDef.postHandler)
  144. ? routeDef.postHandler
  145. : [routeDef.postHandler];
  146. postHandlerHooks.forEach(hook => {
  147. this._hookRegistry.addHook(RouterHookType.POST_HANDLER, hook);
  148. });
  149. }
  150. this.ctorDebug('A new route %s %v was created.', this._method, this._path);
  151. }
  152. /**
  153. * Handle request.
  154. *
  155. * @param {RequestContext} context
  156. * @returns {*}
  157. */
  158. handle(context) {
  159. const debug = this.getDebuggerFor(this.handle);
  160. const requestPath = getRequestPathname(context.request);
  161. debug(
  162. 'Invoking the Route handler for the request %s %v.',
  163. this.method.toUpperCase(),
  164. requestPath,
  165. );
  166. return this._handler(context);
  167. }
  168. }