From ac9248bf7f81c24faef1503bddc5aea3d48fec4d Mon Sep 17 00:00:00 2001 From: Sergey Yarkov Date: Wed, 16 Mar 2022 19:59:17 +0300 Subject: [PATCH] feat: added middlewares --- helpers/string.js | 12 +++++ middlewares.js | 13 ++++++ preloads/routes.js | 11 +++-- router.js | 107 +++++++++++++++++++++++++++++++++++---------- 4 files changed, 116 insertions(+), 27 deletions(-) create mode 100644 helpers/string.js create mode 100644 middlewares.js diff --git a/helpers/string.js b/helpers/string.js new file mode 100644 index 0000000..2fce2d3 --- /dev/null +++ b/helpers/string.js @@ -0,0 +1,12 @@ +class StringHelper { + /** + * Delete the trailing slash from url + * + * @param {string} url + */ + static delTrailingSlash(url) { + return url.slice(-1) === '/' ? url.slice(0, -1) : url; + } +} + +export default StringHelper; diff --git a/middlewares.js b/middlewares.js new file mode 100644 index 0000000..fa26371 --- /dev/null +++ b/middlewares.js @@ -0,0 +1,13 @@ +import http from 'node:http'; +import Response from './response.js'; + +/** + * This middleware just prints text to console + * + * @param {http.ClientRequest} req + * @param {Response} res + * @param {function} next + */ +export const helloMiddleware = (req, res) => { + console.log('[LOG]: Hello from `helloMiddleware` function'); +}; diff --git a/preloads/routes.js b/preloads/routes.js index 7c8abdf..4a2b03c 100644 --- a/preloads/routes.js +++ b/preloads/routes.js @@ -9,6 +9,11 @@ import { getArticlesHandler, } from '../handlers.js'; -router.define('GET', '/api/articles', getArticlesHandler); -router.define('GET', '/api/article/:id', getArticleHandler); -router.define('POST', '/api/article', createArticleHandler); +/** + * Middlewares + */ +import { helloMiddleware } from '../middlewares.js'; + +router.get('/api/articles', getArticlesHandler).middleware([helloMiddleware]); +router.get('/api/article/:id', getArticleHandler).middleware([helloMiddleware]); +router.post('/api/article', createArticleHandler); diff --git a/router.js b/router.js index 2815172..7baea98 100644 --- a/router.js +++ b/router.js @@ -1,5 +1,6 @@ import http from 'node:http'; import { pathToRegexp } from 'path-to-regexp'; +import StringHelper from './helpers/string.js'; class Router { constructor() { @@ -17,24 +18,28 @@ class Router { * @returns {boolean} Is route has been handled */ call(req, res) { - const mask = `${req.method}:${req.url}`; - - /** - * Find the exact route - */ - if (this.routes.hasOwnProperty(mask)) { - this.routes[mask].handler(req, res); - return true; - } - - /** - * Find route by regular expression - */ + const mask = `${req.method}:${StringHelper.delTrailingSlash(req.url)}`; const keys = Object.keys(this.routes); let i = keys.length; while (i--) { - const route = this.routes[keys[i]]; + let route = this.routes[mask]; + + /** + * Find exact route + */ + if (this.routes.hasOwnProperty(mask)) { + this.#middlewares(route, req, res); + route.handler(req, res); + return true; + } + + /* Route url is not exact */ + route = this.routes[keys[i]]; + + /** + * Find route by regular expression + */ if (!route.options.exact && route.options.regexp !== undefined) { const match = req.url.match(route.options.regexp); if (match && keys[i].includes(req.method)) { @@ -44,6 +49,7 @@ class Router { req.params[param.name] = match[1]; }); } + this.#middlewares(route, req, res); route.handler(req, res); return true; } @@ -53,29 +59,82 @@ class Router { return false; } + /** + * This function will execute every middleware of a route + * + * @param {*} route + * @param {http.ClientRequest} req + * @param {Response} res + * @returns + */ + #middlewares(route, req, res) { + if (!route || !route.middlewares || !Array.isArray(route.middleware)) { + return; + } + + const middlewares = route.middlewares.flat(); + if (middlewares.length !== 0) { + for (const middleware of middlewares) { + middleware(req, res); + } + } + } + /** * Add more routes to router store * - * @param {string} url - * @param {function} handler - * @param {boolean} exact + * @param {string} method HTTP Method + * @param {string} url Route url + * @param {function} handler Route handler function */ - define( - method = 'GET', - url, - handler, - options = { exact: true, regexp: undefined, params: undefined } - ) { + define(method = 'GET', url, handler) { + const mask = `${method}:${StringHelper.delTrailingSlash(url)}`; + const options = { exact: true, regexp: undefined, params: undefined }; + if (url.includes(':')) { options.params = []; options.exact = false; options.regexp = pathToRegexp(url, options.params); } - this.routes[`${method}:${url}`] = { + + this.routes[mask] = { method, handler, options, + middlewares: [], }; + + return { + /** + * Store the list of middlewares for each router + * + * @param {function | [function]} middleware + */ + middleware: (middleware) => { + middleware = Array.isArray(middleware) ? middleware : [middleware]; + this.routes[mask].middlewares.push(middleware); + }, + }; + } + + /** + * GET Method route + * + * @param {string} url + * @param {function} handler + */ + get(url, handler) { + return this.define('GET', url, handler); + } + + /** + * POST Method route + * + * @param {string} url + * @param {function} handler + */ + post(url, handler) { + return this.define('POST', url, handler); } }