From 194fa476ca582e8fd72d362b0e2f8815172484c4 Mon Sep 17 00:00:00 2001 From: garrettmills Date: Thu, 30 Jul 2020 09:03:29 -0500 Subject: [PATCH] Add support for regex matching routes --- TODO.txt | 1 - lib/src/http/routing/RegExRoute.ts | 57 ++++++++++++++++++++++++++++++ lib/src/unit/Routing.ts | 9 +++-- 3 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 lib/src/http/routing/RegExRoute.ts diff --git a/TODO.txt b/TODO.txt index 536cad3..ba9fa42 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,4 +1,3 @@ -deepmatch route, regex match route static assets middleware view engine diff --git a/lib/src/http/routing/RegExRoute.ts b/lib/src/http/routing/RegExRoute.ts new file mode 100644 index 0000000..7e4403f --- /dev/null +++ b/lib/src/http/routing/RegExRoute.ts @@ -0,0 +1,57 @@ +import {Route, RouteParameters} from './Route.ts' +import {Logging} from '../../service/logging/Logging.ts' +import {make} from '../../../../di/src/global.ts' + +export class RegExRoute extends Route { + protected key_regex: RegExp + + constructor( + protected base: string, + protected key: string, + ) { + super(base) + this.key_regex = this.build_regex(key) + } + + public get route() { + return this.base + this.key + } + + public match(incoming: string): boolean { + if ( !incoming.toLowerCase().startsWith(this.base) ) return false + incoming = incoming.substr(this.base.length) + + const success = this.key_regex.test(incoming) + + if ( !success ) { + make(Logging).debug(`RegExRoute match failed. (Testing: ${incoming}, Key: ${this.key}, Rex: ${this.key_regex})`) + } + + return success + } + + public build_parameters(incoming: string): RouteParameters { + if ( incoming.toLowerCase().startsWith(this.base) ) incoming = incoming.substr(this.base.length) + + const results = this.key_regex.exec(incoming.toLowerCase()) + if ( !results ) return {} + + const [match, ...wildcards] = results + const params: RouteParameters = {} + let current_wildcard: number = 1 + + for ( const wild of wildcards ) { + params[`$${current_wildcard}`] = wild + current_wildcard += 1 + } + + return params + } + + protected build_regex(key: string) { + if ( !key.startsWith('rex ') ) { + throw new TypeError(`Invalid regular expression route pattern: ${key}`) + } + return new RegExp(key.substr(4)) + } +} diff --git a/lib/src/unit/Routing.ts b/lib/src/unit/Routing.ts index cf7987e..3e9f201 100644 --- a/lib/src/unit/Routing.ts +++ b/lib/src/unit/Routing.ts @@ -14,6 +14,7 @@ import {Logging} from '../service/logging/Logging.ts' import {Canon} from './Canon.ts' import {isBindable} from '../lifecycle/AppClass.ts' import {DeepmatchRoute} from "../http/routing/DeepmatchRoute.ts"; +import {RegExRoute} from "../http/routing/RegExRoute.ts"; export type RouteHandler = (request: Request) => Request | Promise | ResponseFactory | Promise | void | Promise export type RouteHandlers = RouteHandler[] @@ -74,7 +75,7 @@ export default class Routing extends LifecycleUnit { // @ts-ignore this.definitions[base][verb] = this.build_handler(handlers) - this.instances.push(this.build_route(base)) + this.instances.push(this.build_route(base, key)) } } } @@ -136,8 +137,10 @@ export default class Routing extends LifecycleUnit { return `/${joined}`.toLowerCase() } - public build_route(base: string): Route { - if ( !base.includes(':') && !base.includes('*') ) { + public build_route(base: string, key: string): Route { + if ( key.startsWith('rex ') ) { + return new RegExRoute(base.split(key)[0], key) + } else if ( !base.includes(':') && !base.includes('*') ) { return new SimpleRoute(base) } else if ( base.includes('**') ) { return new DeepmatchRoute(base)