route.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import {Errorf} from '@e22m4u/js-format';
  2. import {HOOK_NAME} from './hooks/index.js';
  3. import {HookRegistry} from './hooks/index.js';
  4. import {createDebugger} from './utils/index.js';
  5. import {getRequestPathname} from './utils/index.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. * preHandler: RoutePreHandler|(RoutePreHandler[])
  15. * handler: RouteHandler,
  16. * postHandler: RoutePostHandler|(RoutePostHandler[])
  17. * }} RouteDefinition
  18. */
  19. /**
  20. * Http method.
  21. *
  22. * @type {{
  23. * GET: 'GET',
  24. * POST: 'POST',
  25. * PUT: 'PUT',
  26. * PATCH: 'PATCH',
  27. * DELETE: 'DELETE',
  28. * }}
  29. */
  30. export const HttpMethod = {
  31. GET: 'GET',
  32. POST: 'POST',
  33. PUT: 'PUT',
  34. PATCH: 'PATCH',
  35. DELETE: 'DELETE',
  36. };
  37. /**
  38. * Debugger.
  39. *
  40. * @type {Function}
  41. */
  42. const debug = createDebugger('route');
  43. /**
  44. * Route.
  45. */
  46. export class Route {
  47. /**
  48. * Method.
  49. *
  50. * @type {string}
  51. * @private
  52. */
  53. _method;
  54. /**
  55. * Getter of the method.
  56. *
  57. * @returns {string}
  58. */
  59. get method() {
  60. return this._method;
  61. }
  62. /**
  63. * Path template.
  64. *
  65. * @type {string}
  66. * @private
  67. */
  68. _path;
  69. /**
  70. * Getter of the path.
  71. *
  72. * @returns {string}
  73. */
  74. get path() {
  75. return this._path;
  76. }
  77. /**
  78. * Handler.
  79. *
  80. * @type {RouteHandler}
  81. * @private
  82. */
  83. _handler;
  84. /**
  85. * Getter of the handler.
  86. *
  87. * @returns {*}
  88. */
  89. get handler() {
  90. return this._handler;
  91. }
  92. /**
  93. * Hook registry.
  94. *
  95. * @type {HookRegistry}
  96. * @private
  97. */
  98. _hookRegistry = new HookRegistry();
  99. /**
  100. * Getter of the hook registry.
  101. *
  102. * @returns {HookRegistry}
  103. */
  104. get hookRegistry() {
  105. return this._hookRegistry;
  106. }
  107. /**
  108. * Constructor.
  109. *
  110. * @param {RouteDefinition} routeDef
  111. */
  112. constructor(routeDef) {
  113. if (!routeDef || typeof routeDef !== 'object' || Array.isArray(routeDef))
  114. throw new Errorf(
  115. 'The first parameter of Route.controller ' +
  116. 'should be an Object, but %v given.',
  117. routeDef,
  118. );
  119. if (!routeDef.method || typeof routeDef.method !== 'string')
  120. throw new Errorf(
  121. 'The option "method" of the Route should be ' +
  122. 'a non-empty String, but %v given.',
  123. routeDef.method,
  124. );
  125. this._method = routeDef.method.toUpperCase();
  126. if (typeof routeDef.path !== 'string')
  127. throw new Errorf(
  128. 'The option "path" of the Route should be ' + 'a String, but %v given.',
  129. routeDef.path,
  130. );
  131. this._path = routeDef.path;
  132. if (typeof routeDef.handler !== 'function')
  133. throw new Errorf(
  134. 'The option "handler" of the Route should be ' +
  135. 'a Function, but %v given.',
  136. routeDef.handler,
  137. );
  138. this._handler = routeDef.handler;
  139. if (routeDef.preHandler != null) {
  140. const preHandlerHooks = Array.isArray(routeDef.preHandler)
  141. ? routeDef.preHandler
  142. : [routeDef.preHandler];
  143. preHandlerHooks.forEach(hook => {
  144. this._hookRegistry.addHook(HOOK_NAME.PRE_HANDLER, hook);
  145. });
  146. }
  147. if (routeDef.postHandler != null) {
  148. const postHandlerHooks = Array.isArray(routeDef.postHandler)
  149. ? routeDef.postHandler
  150. : [routeDef.postHandler];
  151. postHandlerHooks.forEach(hook => {
  152. this._hookRegistry.addHook(HOOK_NAME.POST_HANDLER, hook);
  153. });
  154. }
  155. }
  156. /**
  157. * Handle request.
  158. *
  159. * @param {RequestContext} context
  160. * @returns {*}
  161. */
  162. handle(context) {
  163. const requestPath = getRequestPathname(context.req);
  164. debug(
  165. 'Invoking the Route handler for the request %s %v.',
  166. this.method.toUpperCase(),
  167. requestPath,
  168. );
  169. return this._handler(context);
  170. }
  171. }