2021-06-03 03:36:25 +00:00
|
|
|
import {AppClass} from '../../lifecycle/AppClass'
|
|
|
|
import {HTTPMethod, Request} from '../lifecycle/Request'
|
|
|
|
import {Application} from '../../lifecycle/Application'
|
|
|
|
import {RouteGroup} from './RouteGroup'
|
|
|
|
import {ResponseFactory} from '../response/ResponseFactory'
|
|
|
|
import {Response} from '../lifecycle/Response'
|
|
|
|
import {Controllers} from '../../service/Controllers'
|
|
|
|
import {ErrorWithContext, Collection} from '../../util'
|
|
|
|
import {Container} from '../../di'
|
|
|
|
import {Controller} from '../Controller'
|
|
|
|
import {Middlewares} from '../../service/Middlewares'
|
|
|
|
import {Middleware} from './Middleware'
|
|
|
|
import {Config} from '../../service/Config'
|
2021-03-08 17:08:56 +00:00
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/**
|
|
|
|
* Type alias for an item that is a valid response object, or lack thereof.
|
|
|
|
*/
|
2021-03-08 17:08:56 +00:00
|
|
|
export type ResponseObject = ResponseFactory | string | number | void | any | Promise<ResponseObject>
|
2021-03-25 13:50:13 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Type alias for an item that defines a direct route handler.
|
|
|
|
*/
|
2021-03-08 17:08:56 +00:00
|
|
|
export type RouteHandler = ((request: Request, response: Response) => ResponseObject) | ((request: Request) => ResponseObject) | (() => ResponseObject) | string
|
2021-03-25 13:50:13 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Type alias for a function that applies a route handler to the request.
|
|
|
|
* The goal is to transform RouteHandlers to ResolvedRouteHandler.
|
|
|
|
*/
|
2021-03-08 17:08:56 +00:00
|
|
|
export type ResolvedRouteHandler = (request: Request) => ResponseObject
|
2021-03-08 15:00:43 +00:00
|
|
|
|
|
|
|
|
2021-03-09 15:42:19 +00:00
|
|
|
// TODO domains, named routes - support this on groups as well
|
2021-03-08 15:00:43 +00:00
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/**
|
|
|
|
* A class that can be used to build and reference dynamic routes in the application.
|
|
|
|
*
|
|
|
|
* Routes can be defined in nested groups, with prefixes and middleware handlers.
|
|
|
|
*
|
|
|
|
* @example
|
|
|
|
* ```typescript
|
|
|
|
* Route.post('/api/v1/ping', (request: Request) => {
|
|
|
|
* return 'pong!'
|
|
|
|
* })
|
|
|
|
*
|
|
|
|
* Route.group('/api/v2', () => {
|
|
|
|
* Route.get('/status', 'controller::api:v2:Status.getStatus').pre('auth:UserOnly')
|
|
|
|
* })
|
|
|
|
* ```
|
|
|
|
*/
|
2021-03-08 15:00:43 +00:00
|
|
|
export class Route extends AppClass {
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Routes that have been created and registered in the application. */
|
2021-03-08 15:00:43 +00:00
|
|
|
private static registeredRoutes: Route[] = []
|
2021-03-25 13:50:13 +00:00
|
|
|
|
|
|
|
/** Groups of routes that have been registered with the application. */
|
2021-03-08 15:00:43 +00:00
|
|
|
private static registeredGroups: RouteGroup[] = []
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/**
|
|
|
|
* The current nested group stack. This is used internally when compiling the routes by nested group.
|
|
|
|
* @private
|
|
|
|
*/
|
2021-03-08 15:00:43 +00:00
|
|
|
private static compiledGroupStack: RouteGroup[] = []
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Register a route group handler. */
|
2021-06-03 03:36:25 +00:00
|
|
|
public static registerGroup(group: RouteGroup): void {
|
2021-03-08 15:00:43 +00:00
|
|
|
this.registeredGroups.push(group)
|
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/**
|
|
|
|
* Load and compile all of the registered routes and their groups, accounting
|
|
|
|
* for nested groups and resolving handlers.
|
|
|
|
*
|
|
|
|
* This function attempts to resolve the route handlers ahead of time to cache
|
|
|
|
* them and also expose any handler resolution errors that might happen at runtime.
|
|
|
|
*/
|
2021-03-08 15:00:43 +00:00
|
|
|
public static async compile(): Promise<Route[]> {
|
|
|
|
let registeredRoutes = this.registeredRoutes
|
|
|
|
const registeredGroups = this.registeredGroups
|
|
|
|
|
|
|
|
this.registeredRoutes = []
|
|
|
|
this.registeredGroups = []
|
|
|
|
|
2021-03-09 15:55:18 +00:00
|
|
|
const configService = <Config> Container.getContainer().make(Config)
|
|
|
|
const globalMiddleware = configService.get('server.middleware.global', {})
|
|
|
|
|
2021-03-08 15:00:43 +00:00
|
|
|
const stack = [...this.compiledGroupStack].reverse()
|
|
|
|
for ( const route of registeredRoutes ) {
|
|
|
|
for ( const group of stack ) {
|
|
|
|
route.prepend(group.prefix)
|
2021-03-09 15:42:19 +00:00
|
|
|
group.getGroupMiddlewareDefinitions()
|
|
|
|
.each(def => route.prependMiddleware(def))
|
2021-03-08 15:00:43 +00:00
|
|
|
}
|
2021-03-08 17:08:56 +00:00
|
|
|
|
2021-03-09 15:42:19 +00:00
|
|
|
for ( const group of this.compiledGroupStack ) {
|
|
|
|
group.getGroupMiddlewareDefinitions()
|
|
|
|
.each(def => route.appendMiddleware(def))
|
|
|
|
}
|
|
|
|
|
2021-03-09 15:55:18 +00:00
|
|
|
// Add the global pre- and post- middleware
|
|
|
|
if ( Array.isArray(globalMiddleware?.pre) ) {
|
|
|
|
const globalPre = [...globalMiddleware.pre].reverse()
|
|
|
|
for ( const item of globalPre ) {
|
|
|
|
if ( typeof item !== 'string' ) {
|
|
|
|
throw new ErrorWithContext(`Invalid global pre-middleware definition. Global middleware must be string-references.`, {
|
|
|
|
configKey: 'server.middleware.global.pre',
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
route.prependMiddleware({
|
|
|
|
stage: 'pre',
|
|
|
|
handler: item,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( Array.isArray(globalMiddleware?.post) ) {
|
|
|
|
const globalPost = [...globalMiddleware.post]
|
|
|
|
for ( const item of globalPost ) {
|
|
|
|
if ( typeof item !== 'string' ) {
|
|
|
|
throw new ErrorWithContext(`Invalid global post-middleware definition. Global middleware must be string-references.`, {
|
|
|
|
configKey: 'server.middleware.global.post',
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
route.appendMiddleware({
|
|
|
|
stage: 'post',
|
|
|
|
handler: item,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-09 15:42:19 +00:00
|
|
|
route.resolvePreflight() // Try to resolve here to catch any errors at boot-time and pre-compile
|
|
|
|
route.resolveHandler()
|
|
|
|
route.resolvePostflight()
|
2021-03-08 15:00:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for ( const group of registeredGroups ) {
|
|
|
|
this.compiledGroupStack.push(group)
|
|
|
|
await group.group()
|
|
|
|
|
|
|
|
const childCompilation = await this.compile()
|
|
|
|
registeredRoutes = registeredRoutes.concat(childCompilation)
|
|
|
|
|
|
|
|
this.compiledGroupStack.pop()
|
|
|
|
}
|
|
|
|
|
|
|
|
return registeredRoutes
|
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/**
|
|
|
|
* Create a new route on the given endpoint for the given HTTP verb.
|
|
|
|
* @param method
|
|
|
|
* @param definition
|
|
|
|
* @param handler
|
|
|
|
*/
|
2021-06-03 03:36:25 +00:00
|
|
|
public static endpoint(method: HTTPMethod | HTTPMethod[], definition: string, handler: RouteHandler): Route {
|
2021-03-08 15:00:43 +00:00
|
|
|
const route = new Route(method, handler, definition)
|
|
|
|
this.registeredRoutes.push(route)
|
|
|
|
return route
|
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/**
|
|
|
|
* Create a new GET route on the given endpoint.
|
|
|
|
* @param definition
|
|
|
|
* @param handler
|
|
|
|
*/
|
2021-06-03 03:36:25 +00:00
|
|
|
public static get(definition: string, handler: RouteHandler): Route {
|
2021-03-08 15:00:43 +00:00
|
|
|
return this.endpoint('get', definition, handler)
|
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Create a new POST route on the given endpoint. */
|
2021-06-03 03:36:25 +00:00
|
|
|
public static post(definition: string, handler: RouteHandler): Route {
|
2021-03-08 15:00:43 +00:00
|
|
|
return this.endpoint('post', definition, handler)
|
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Create a new PUT route on the given endpoint. */
|
2021-06-03 03:36:25 +00:00
|
|
|
public static put(definition: string, handler: RouteHandler): Route {
|
2021-03-08 15:00:43 +00:00
|
|
|
return this.endpoint('put', definition, handler)
|
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Create a new PATCH route on the given endpoint. */
|
2021-06-03 03:36:25 +00:00
|
|
|
public static patch(definition: string, handler: RouteHandler): Route {
|
2021-03-08 15:00:43 +00:00
|
|
|
return this.endpoint('patch', definition, handler)
|
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Create a new DELETE route on the given endpoint. */
|
2021-06-03 03:36:25 +00:00
|
|
|
public static delete(definition: string, handler: RouteHandler): Route {
|
2021-03-08 15:00:43 +00:00
|
|
|
return this.endpoint('delete', definition, handler)
|
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Create a new route on all HTTP verbs, on the given endpoint. */
|
2021-06-03 03:36:25 +00:00
|
|
|
public static any(definition: string, handler: RouteHandler): Route {
|
2021-03-08 15:00:43 +00:00
|
|
|
return this.endpoint(['get', 'put', 'patch', 'post', 'delete'], definition, handler)
|
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Create a new route group with the given prefix. */
|
2021-06-03 03:36:25 +00:00
|
|
|
public static group(prefix: string, group: () => void | Promise<void>): RouteGroup {
|
2021-03-08 15:00:43 +00:00
|
|
|
const grp = <RouteGroup> Application.getApplication().make(RouteGroup, group, prefix)
|
|
|
|
this.registeredGroups.push(grp)
|
|
|
|
return grp
|
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Middlewares that should be applied to this route. */
|
2021-06-03 03:36:25 +00:00
|
|
|
protected middlewares: Collection<{ stage: 'pre' | 'post', handler: RouteHandler }> = new Collection<{stage: 'pre' | 'post'; handler: RouteHandler}>()
|
2021-03-25 13:50:13 +00:00
|
|
|
|
|
|
|
/** Pre-compiled route handlers for the pre-middleware for this route. */
|
2021-06-03 03:36:25 +00:00
|
|
|
protected compiledPreflight?: ResolvedRouteHandler[]
|
2021-03-25 13:50:13 +00:00
|
|
|
|
|
|
|
/** Pre-compiled route handlers for the post-middleware for this route. */
|
2021-06-03 03:36:25 +00:00
|
|
|
protected compiledHandler?: ResolvedRouteHandler
|
2021-03-25 13:50:13 +00:00
|
|
|
|
|
|
|
/** Pre-compiled route handler for the main route handler for this route. */
|
2021-06-03 03:36:25 +00:00
|
|
|
protected compiledPostflight?: ResolvedRouteHandler[]
|
2021-03-09 15:42:19 +00:00
|
|
|
|
2021-03-08 15:00:43 +00:00
|
|
|
constructor(
|
2021-03-25 13:50:13 +00:00
|
|
|
/** The HTTP method(s) that this route listens on. */
|
2021-03-08 15:00:43 +00:00
|
|
|
protected method: HTTPMethod | HTTPMethod[],
|
2021-03-25 13:50:13 +00:00
|
|
|
|
|
|
|
/** The primary handler of this route. */
|
2021-03-08 17:08:56 +00:00
|
|
|
protected readonly handler: RouteHandler,
|
2021-03-25 13:50:13 +00:00
|
|
|
|
|
|
|
/** The route path this route listens on. */
|
2021-06-03 03:36:25 +00:00
|
|
|
protected route: string,
|
|
|
|
) {
|
|
|
|
super()
|
|
|
|
}
|
2021-03-08 15:00:43 +00:00
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/**
|
|
|
|
* Returns true if this route matches the given HTTP verb and request path.
|
|
|
|
* @param method
|
|
|
|
* @param potential
|
|
|
|
*/
|
2021-03-08 15:00:43 +00:00
|
|
|
public match(method: HTTPMethod, potential: string): boolean {
|
2021-06-03 03:36:25 +00:00
|
|
|
if ( Array.isArray(this.method) && !this.method.includes(method) ) {
|
|
|
|
return false
|
|
|
|
} else if ( !Array.isArray(this.method) && this.method !== method ) {
|
|
|
|
return false
|
|
|
|
}
|
2021-03-08 15:00:43 +00:00
|
|
|
|
2021-06-03 03:36:25 +00:00
|
|
|
return Boolean(this.extract(potential))
|
2021-03-08 15:00:43 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/**
|
|
|
|
* Given a request path, try to extract this route's paramters from the path string.
|
|
|
|
*
|
|
|
|
* @example
|
|
|
|
* For route `/foo/:bar/baz` and input `/foo/bob/baz`, extracts:
|
|
|
|
*
|
|
|
|
* ```typescript
|
|
|
|
* {
|
|
|
|
* bar: 'bob'
|
|
|
|
* }
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @param potential
|
|
|
|
*/
|
2021-03-08 15:00:43 +00:00
|
|
|
public extract(potential: string): {[key: string]: string} | undefined {
|
|
|
|
const routeParts = (this.route.startsWith('/') ? this.route.substr(1) : this.route).split('/')
|
|
|
|
const potentialParts = (potential.startsWith('/') ? potential.substr(1) : potential).split('/')
|
|
|
|
|
|
|
|
const params: any = {}
|
|
|
|
let wildcardIdx = 0
|
|
|
|
|
|
|
|
for ( let i = 0; i < routeParts.length; i += 1 ) {
|
|
|
|
const part = routeParts[i]
|
|
|
|
|
|
|
|
if ( part === '**' ) {
|
|
|
|
params[wildcardIdx] = potentialParts.slice(i).join('/')
|
|
|
|
return params
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( (potentialParts.length - 1) < i ) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( part === '*' ) {
|
|
|
|
params[wildcardIdx] = potentialParts[i]
|
|
|
|
wildcardIdx += 1
|
|
|
|
} else if ( part.startsWith(':') ) {
|
|
|
|
params[part.substr(1)] = potentialParts[i]
|
|
|
|
} else if ( potentialParts[i] !== part ) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we got here, we didn't find a **
|
|
|
|
// So, if the lengths are different, fail
|
2021-06-03 03:36:25 +00:00
|
|
|
if ( routeParts.length !== potentialParts.length ) {
|
|
|
|
return
|
|
|
|
}
|
2021-03-08 15:00:43 +00:00
|
|
|
return params
|
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/**
|
|
|
|
* Try to pre-compile and return the preflight handlers for this route.
|
|
|
|
*/
|
2021-03-09 15:42:19 +00:00
|
|
|
public resolvePreflight(): ResolvedRouteHandler[] {
|
2021-06-03 03:36:25 +00:00
|
|
|
if ( !this.compiledPreflight ) {
|
|
|
|
this.compiledPreflight = this.resolveMiddlewareHandlersForStage('pre')
|
2021-03-09 15:42:19 +00:00
|
|
|
}
|
|
|
|
|
2021-06-03 03:36:25 +00:00
|
|
|
return this.compiledPreflight
|
2021-03-09 15:42:19 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/**
|
|
|
|
* Try to pre-compile and return the postflight handlers for this route.
|
|
|
|
*/
|
2021-03-09 15:42:19 +00:00
|
|
|
public resolvePostflight(): ResolvedRouteHandler[] {
|
2021-06-03 03:36:25 +00:00
|
|
|
if ( !this.compiledPostflight ) {
|
|
|
|
this.compiledPostflight = this.resolveMiddlewareHandlersForStage('post')
|
2021-03-09 15:42:19 +00:00
|
|
|
}
|
|
|
|
|
2021-06-03 03:36:25 +00:00
|
|
|
return this.compiledPostflight
|
2021-03-09 15:42:19 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/**
|
|
|
|
* Try to pre-compile and return the main handler for this route.
|
|
|
|
*/
|
2021-03-08 17:08:56 +00:00
|
|
|
public resolveHandler(): ResolvedRouteHandler {
|
2021-06-03 03:36:25 +00:00
|
|
|
if ( !this.compiledHandler ) {
|
|
|
|
this.compiledHandler = this.compileResolvedHandler()
|
2021-03-09 15:42:19 +00:00
|
|
|
}
|
|
|
|
|
2021-06-03 03:36:25 +00:00
|
|
|
return this.compiledHandler
|
2021-03-09 15:42:19 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Register the given middleware as a preflight handler for this route. */
|
2021-06-03 03:36:25 +00:00
|
|
|
pre(middleware: RouteHandler): this {
|
2021-03-09 15:42:19 +00:00
|
|
|
this.middlewares.push({
|
|
|
|
stage: 'pre',
|
2021-06-03 03:36:25 +00:00
|
|
|
handler: middleware,
|
2021-03-09 15:42:19 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Register the given middleware as a postflight handler for this route. */
|
2021-06-03 03:36:25 +00:00
|
|
|
post(middleware: RouteHandler): this {
|
2021-03-09 15:42:19 +00:00
|
|
|
this.middlewares.push({
|
|
|
|
stage: 'post',
|
|
|
|
handler: middleware,
|
|
|
|
})
|
|
|
|
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Prefix the route's path with the given prefix, normalizing `/` characters. */
|
2021-06-03 03:36:25 +00:00
|
|
|
private prepend(prefix: string): this {
|
|
|
|
if ( !prefix.endsWith('/') ) {
|
|
|
|
prefix = `${prefix}/`
|
|
|
|
}
|
|
|
|
if ( this.route.startsWith('/') ) {
|
|
|
|
this.route = this.route.substring(1)
|
|
|
|
}
|
2021-03-09 15:42:19 +00:00
|
|
|
this.route = `${prefix}${this.route}`
|
2021-06-03 03:36:25 +00:00
|
|
|
return this
|
2021-03-09 15:42:19 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Add the given middleware item to the beginning of the preflight handlers. */
|
2021-06-03 03:36:25 +00:00
|
|
|
private prependMiddleware(def: { stage: 'pre' | 'post', handler: RouteHandler }): void {
|
2021-03-09 15:42:19 +00:00
|
|
|
this.middlewares.prepend(def)
|
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Add the given middleware item to the end of the postflight handlers. */
|
2021-06-03 03:36:25 +00:00
|
|
|
private appendMiddleware(def: { stage: 'pre' | 'post', handler: RouteHandler }): void {
|
2021-03-09 15:42:19 +00:00
|
|
|
this.middlewares.push(def)
|
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/**
|
|
|
|
* Resolve and return the route handler for this route.
|
|
|
|
* @private
|
|
|
|
*/
|
2021-06-03 03:36:25 +00:00
|
|
|
private compileResolvedHandler(): ResolvedRouteHandler {
|
|
|
|
const handler = this.handler
|
|
|
|
if ( typeof handler !== 'string' ) {
|
2021-03-08 17:08:56 +00:00
|
|
|
return (request: Request) => {
|
2021-06-03 03:36:25 +00:00
|
|
|
return handler(request, request.response)
|
2021-03-08 17:08:56 +00:00
|
|
|
}
|
|
|
|
} else {
|
2021-06-03 03:36:25 +00:00
|
|
|
const parts = handler.split('.')
|
2021-03-09 15:42:19 +00:00
|
|
|
if ( parts.length < 2 ) {
|
2021-03-08 17:08:56 +00:00
|
|
|
const e = new ErrorWithContext('Route handler does not specify a method name.')
|
|
|
|
e.context = {
|
2021-06-03 03:36:25 +00:00
|
|
|
handler,
|
2021-03-08 17:08:56 +00:00
|
|
|
}
|
|
|
|
throw e
|
|
|
|
}
|
|
|
|
|
|
|
|
const [controllerName, methodName] = parts
|
|
|
|
|
|
|
|
const controllersService = <Controllers> this.make(Controllers)
|
|
|
|
const controllerClass = controllersService.get(controllerName)
|
|
|
|
if ( !controllerClass ) {
|
|
|
|
const e = new ErrorWithContext('Controller not found for route handler.')
|
|
|
|
e.context = {
|
2021-06-03 03:36:25 +00:00
|
|
|
handler,
|
2021-03-08 17:08:56 +00:00
|
|
|
controllerName,
|
|
|
|
methodName,
|
|
|
|
}
|
|
|
|
throw e
|
|
|
|
}
|
|
|
|
|
|
|
|
return (request: Request) => {
|
|
|
|
// If not a function, then we got a string reference to a controller method
|
|
|
|
// So, we need to use the request container to instantiate the controller
|
|
|
|
// and bind the method
|
|
|
|
const controller = <Controller> request.make(controllerClass, request)
|
|
|
|
const method = controller.getBoundMethod(methodName)
|
|
|
|
return method()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/**
|
|
|
|
* Resolve and return the route handlers for the given pre- or post-flight stage.
|
|
|
|
* @param stage
|
|
|
|
* @private
|
|
|
|
*/
|
2021-03-09 15:42:19 +00:00
|
|
|
private resolveMiddlewareHandlersForStage(stage: 'pre' | 'post'): ResolvedRouteHandler[] {
|
|
|
|
return this.middlewares.where('stage', '=', stage)
|
|
|
|
.map<ResolvedRouteHandler>(def => {
|
2021-06-03 03:36:25 +00:00
|
|
|
const handler = def.handler
|
|
|
|
if ( typeof handler !== 'string' ) {
|
2021-03-09 15:42:19 +00:00
|
|
|
return (request: Request) => {
|
2021-06-03 03:36:25 +00:00
|
|
|
return handler(request, request.response)
|
2021-03-09 15:42:19 +00:00
|
|
|
}
|
|
|
|
} else {
|
2021-06-03 03:36:25 +00:00
|
|
|
const parts = handler.split('.')
|
2021-03-09 15:42:19 +00:00
|
|
|
if ( parts.length < 2 ) {
|
|
|
|
parts.push('apply') // default middleware method name, if none provided
|
|
|
|
}
|
|
|
|
|
|
|
|
const [middlewareName, methodName] = parts
|
|
|
|
|
|
|
|
const middlewaresService = <Middlewares> this.make(Middlewares)
|
|
|
|
const middlewareClass = middlewaresService.get(middlewareName)
|
|
|
|
if ( !middlewareClass ) {
|
|
|
|
const e = new ErrorWithContext('Middleware not found for route handler.')
|
|
|
|
e.context = {
|
2021-06-03 03:36:25 +00:00
|
|
|
handler,
|
2021-03-09 15:42:19 +00:00
|
|
|
middlewareName,
|
|
|
|
methodName,
|
|
|
|
}
|
|
|
|
throw e
|
|
|
|
}
|
|
|
|
|
|
|
|
return (request: Request) => {
|
|
|
|
// If not a function, then we got a string reference to a middleware method
|
|
|
|
// So, we need to use the request container to instantiate the middleware
|
|
|
|
// and bind the method
|
|
|
|
const middleware = <Middleware> request.make(middlewareClass, request)
|
|
|
|
const method = middleware.getBoundMethod(methodName)
|
|
|
|
return method()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.toArray()
|
2021-03-08 15:00:43 +00:00
|
|
|
}
|
|
|
|
|
2021-03-25 13:50:13 +00:00
|
|
|
/** Cast the route to an intelligible string. */
|
2021-06-03 03:36:25 +00:00
|
|
|
toString(): string {
|
2021-03-08 15:00:43 +00:00
|
|
|
const method = Array.isArray(this.method) ? this.method : [this.method]
|
|
|
|
return `${method.join('|')} -> ${this.route}`
|
|
|
|
}
|
|
|
|
}
|