UNPKG

ravel

Version:

Ravel Rapid Application Development Framework

169 lines (140 loc) 4.43 kB
'use strict'; const { RouteTreeRoot, Methods } = require('../util/route-tree'); const $err = require('../util/application_error'); const sRouteTree = Symbol.for('_routeTree'); /** * Internal router for Ravel. A deterministic router which allows for routing * that takes place independent of route declaration order. * * @private */ class Router { /** * Constructs a new Router for Ravel. * * @private */ constructor() { this[sRouteTree] = new RouteTreeRoot(); } /** * Define a new HEAD route * * @private * @param {string} pattern - The matching pattern for this route. * @param {AsyncFunction[]} middleware - One or more middleware functions to attach to this route */ head(pattern, ...middleware) { this[sRouteTree].addRoute(Methods.HEAD, pattern, middleware); } /** * Define a new GET route * * @private * @param {string} pattern - The matching pattern for this route. * @param {AsyncFunction[]} middleware - One or more middleware functions to attach to this route */ get(pattern, ...middleware) { this[sRouteTree].addRoute(Methods.GET, pattern, middleware); } /** * Define a new PATCH route * * @private * @param {string} pattern - The matching pattern for this route. * @param {AsyncFunction[]} middleware - One or more middleware functions to attach to this route */ patch(pattern, ...middleware) { this[sRouteTree].addRoute(Methods.PATCH, pattern, middleware); } /** * Define a new POST route * * @private * @param {string} pattern - The matching pattern for this route. * @param {AsyncFunction[]} middleware - One or more middleware functions to attach to this route */ post(pattern, ...middleware) { this[sRouteTree].addRoute(Methods.POST, pattern, middleware); } /** * Define a new PUT route * * @private * @param {string} pattern - The matching pattern for this route. * @param {AsyncFunction[]} middleware - One or more middleware functions to attach to this route */ put(pattern, ...middleware) { this[sRouteTree].addRoute(Methods.PUT, pattern, middleware); } /** * Define a new DELETE route * * @private * @param {string} pattern - The matching pattern for this route. * @param {AsyncFunction[]} middleware - One or more middleware functions to attach to this route */ delete(pattern, ...middleware) { this[sRouteTree].addRoute(Methods.DELETE, pattern, middleware); } /** * Define a new catch-all route * * @private * @param {string} method - GET/POST/PUT/PATCH/DELETE. * @param {string} pattern - The matching pattern for this route. * @param {AsyncFunction[]} middleware - One or more middleware functions to attach to this route */ catchAll(method, pattern, ...middleware) { this[sRouteTree].addRoute(Methods[method], pattern, middleware, true); } /** * Construct and return routing middleware * * @private * @returns {AsyncFunction} - Koa-compatible routing middleware. */ middleware() { this[sRouteTree].sort(); return async (ctx, next) => { if (ctx.method === 'OPTIONS') { ctx.status = 200; ctx.set('Allow', ['OPTIONS', ...this[sRouteTree].allowedMethods()].join(', ')); } else { let match; // try to find a match to the request url try { match = this[sRouteTree].match(ctx.method, ctx.path); if (match === null) { ctx.status = 404; return; } } catch (err) { if (err instanceof $err.IllegalValue) { ctx.status = 405; ctx.set('Allow', ['OPTIONS', ...this[sRouteTree].allowedMethods()].join(', ')); return; } throw err; } // block use of body-setting methods if request method is HEAD if (ctx.method === 'HEAD') { Object.defineProperty(ctx.response, 'body', { get: function () { return null; }, set: function (v) { if (v !== null && v !== undefined) { throw new $err.General('Cannot use ctx.body within HEAD requests.'); } } }); } // otherwise, run the matching middleware ctx.params = match.params; await match.composedMiddleware(ctx, next); } }; } } module.exports = Router;