Add logic for resolving route handlers

This commit is contained in:
2021-03-08 11:08:56 -06:00
parent a9ffa771dc
commit 9747d40659
8 changed files with 80 additions and 14 deletions

View File

@@ -1,3 +1,12 @@
import {AppClass} from "../lifecycle/AppClass";
import {Request} from "./lifecycle/Request";
export class Controller extends AppClass {}
export class Controller extends AppClass {
constructor(
protected readonly request: Request
) { super() }
protected container() {
return this.request
}
}

View File

@@ -1,8 +1,9 @@
import {ErrorWithContext} from "@extollo/util";
import {Route} from "./Route";
import {ResolvedRouteHandler, Route} from "./Route";
export class ActivatedRoute {
public readonly params: {[key: string]: string}
public readonly handler: ResolvedRouteHandler
constructor(
public readonly route: Route,
@@ -19,5 +20,6 @@ export class ActivatedRoute {
}
this.params = params
this.handler = route.resolveHandler()
}
}

View File

@@ -2,8 +2,16 @@ 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} from "@extollo/util";
import {Controller} from "../Controller";
export type ResponseObject = ResponseFactory | string | number | void | any | Promise<ResponseObject>
export type RouteHandler = ((request: Request, response: Response) => ResponseObject) | ((request: Request) => ResponseObject) | (() => ResponseObject) | string
export type ResolvedRouteHandler = (request: Request) => ResponseObject
export type RouteHandler = (request: Request) => void | Promise<void> // FIXME want to do some improvements here
// TODO middleware, domains, named routes - support this on groups as well
@@ -29,6 +37,8 @@ export class Route extends AppClass {
for ( const group of stack ) {
route.prepend(group.prefix)
}
route.resolveHandler() // Try to resolve here to catch any errors at boot-time
}
for ( const group of registeredGroups ) {
@@ -82,7 +92,7 @@ export class Route extends AppClass {
constructor(
protected method: HTTPMethod | HTTPMethod[],
protected handler: RouteHandler,
protected readonly handler: RouteHandler,
protected route: string
) { super() }
@@ -128,6 +138,47 @@ export class Route extends AppClass {
return params
}
public resolveHandler(): ResolvedRouteHandler {
if ( typeof this.handler !== 'string' ) {
return (request: Request) => {
// @ts-ignore
return this.handler(request, request.response)
}
} else {
const parts = this.handler.split('.')
if ( parts.length < 1 ) {
const e = new ErrorWithContext('Route handler does not specify a method name.')
e.context = {
handler: this.handler
}
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 = {
handler: this.handler,
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()
}
}
}
private prepend(prefix: string) {
if ( !prefix.endsWith('/') ) prefix = `${prefix}/`
if ( this.route.startsWith('/') ) this.route = this.route.substring(1)