feat: added middlewares

This commit is contained in:
Sergey Yarkov 2022-03-16 19:59:17 +03:00
parent 93c72e36a8
commit ac9248bf7f
4 changed files with 116 additions and 27 deletions

12
helpers/string.js Normal file
View File

@ -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;

13
middlewares.js Normal file
View File

@ -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');
};

View File

@ -9,6 +9,11 @@ import {
getArticlesHandler, getArticlesHandler,
} from '../handlers.js'; } from '../handlers.js';
router.define('GET', '/api/articles', getArticlesHandler); /**
router.define('GET', '/api/article/:id', getArticleHandler); * Middlewares
router.define('POST', '/api/article', createArticleHandler); */
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);

107
router.js
View File

@ -1,5 +1,6 @@
import http from 'node:http'; import http from 'node:http';
import { pathToRegexp } from 'path-to-regexp'; import { pathToRegexp } from 'path-to-regexp';
import StringHelper from './helpers/string.js';
class Router { class Router {
constructor() { constructor() {
@ -17,24 +18,28 @@ class Router {
* @returns {boolean} Is route has been handled * @returns {boolean} Is route has been handled
*/ */
call(req, res) { call(req, res) {
const mask = `${req.method}:${req.url}`; const mask = `${req.method}:${StringHelper.delTrailingSlash(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 keys = Object.keys(this.routes); const keys = Object.keys(this.routes);
let i = keys.length; let i = keys.length;
while (i--) { 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) { if (!route.options.exact && route.options.regexp !== undefined) {
const match = req.url.match(route.options.regexp); const match = req.url.match(route.options.regexp);
if (match && keys[i].includes(req.method)) { if (match && keys[i].includes(req.method)) {
@ -44,6 +49,7 @@ class Router {
req.params[param.name] = match[1]; req.params[param.name] = match[1];
}); });
} }
this.#middlewares(route, req, res);
route.handler(req, res); route.handler(req, res);
return true; return true;
} }
@ -53,29 +59,82 @@ class Router {
return false; 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 * Add more routes to router store
* *
* @param {string} url * @param {string} method HTTP Method
* @param {function} handler * @param {string} url Route url
* @param {boolean} exact * @param {function} handler Route handler function
*/ */
define( define(method = 'GET', url, handler) {
method = 'GET', const mask = `${method}:${StringHelper.delTrailingSlash(url)}`;
url, const options = { exact: true, regexp: undefined, params: undefined };
handler,
options = { exact: true, regexp: undefined, params: undefined }
) {
if (url.includes(':')) { if (url.includes(':')) {
options.params = []; options.params = [];
options.exact = false; options.exact = false;
options.regexp = pathToRegexp(url, options.params); options.regexp = pathToRegexp(url, options.params);
} }
this.routes[`${method}:${url}`] = {
this.routes[mask] = {
method, method,
handler, handler,
options, 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);
} }
} }