Request handlers & error response factory!

This commit is contained in:
garrettmills
2020-07-29 22:01:20 -05:00
parent 48f5da1747
commit a04f083dbb
11 changed files with 240 additions and 22 deletions

View File

@@ -1,7 +1,8 @@
import {Request} from '../Request.ts'
import Kernel from './Kernel.ts'
import AppClass from '../../lifecycle/AppClass.ts'
export default class Module {
export default class Module extends AppClass {
public async match(request: Request): Promise<boolean> {
return true
}

View File

@@ -0,0 +1,53 @@
import Module from '../Module.ts'
import {Injectable} from '../../../../../di/src/decorator/Injection.ts'
import Kernel from '../Kernel.ts'
import {Logging} from '../../../service/logging/Logging.ts'
import {Request} from '../../Request.ts'
import ResponseFactory from '../../response/ResponseFactory.ts'
import ErrorResponseFactory from "../../response/ErrorResponseFactory.ts";
@Injectable()
export default class ApplyRouteHandlers extends Module {
public static register(kernel: Kernel) {
kernel.register(this).core()
}
constructor(
protected readonly logger: Logging,
) {
super()
}
public async apply(request: Request): Promise<Request> {
if (
!request.route
|| !request.route.handlers
|| request.route.handlers.length < 1
) {
return request
}
let current_request: Request = request
for ( const handler of request.route.handlers ) {
try {
const result = await handler(current_request)
if ( result instanceof Request ) {
// If we got a request instance back, use that for further handlers
current_request = result
} else if ( result instanceof ResponseFactory ) {
// If we got a response factory back, write the response and move along
return await result.write(current_request)
}
} catch (e) {
this.logger.error('Error encountered while applying request handlers!')
this.logger.error(e)
// TODO determine response type (html | json, &c.)
const error_response: ErrorResponseFactory = this.make(ErrorResponseFactory, e)
return await error_response.write(request)
}
}
return current_request
}
}

View File

@@ -40,7 +40,7 @@ export default class MountActivatedRoute extends Module {
}
}
const activated_route: ActivatedRoute | undefined = this.routing.build(incoming)
const activated_route: ActivatedRoute | undefined = this.routing.build(incoming, request.method.toLowerCase())
if ( activated_route ) {
this.logger.verbose(`Resolved activated route: ${activated_route.route.route}`)
request.route = activated_route

View File

@@ -0,0 +1,50 @@
import ResponseFactory from "./ResponseFactory.ts";
import {Request} from "../Request.ts";
export default class ErrorResponseFactory extends ResponseFactory {
constructor(
public readonly error: Error,
public readonly output: 'json' | 'html' = 'html',
public readonly status: number = 500,
) {
super()
}
public async write(request: Request): Promise<Request> {
request.response.status = this.status
if ( this.output === 'json' ) {
request.response.headers.set('Content-Type', 'application/json')
request.response.body = this.build_json(this.error)
} else if ( this.output === 'html' ) {
request.response.headers.set('Content-Type', 'text/html')
request.response.body = this.build_html(this.error)
}
return request
}
protected build_html(error: Error) {
return `
<b>Sorry, an unexpected error occurred while processing your request.</b>
<br>
<pre><code>
Name: ${error.name}
Message: ${error.message}
Stack trace:
- ${error.stack ? error.stack.split(/\s+at\s+/).slice(1).join('<br> - ') : 'none'}
</code></pre>
`
}
protected build_json(error: Error) {
return JSON.stringify({
success: false,
error: {
name: error.name,
message: error.message,
stack: error.stack ? error.stack.split(/\s+at\s+/).slice(1) : []
}
})
}
}

View File

@@ -9,6 +9,7 @@ export default class JSONResponseFactory extends ResponseFactory {
}
public async write(request: Request): Promise<Request> {
request.response.headers.set('Content-Type', 'application/json')
request.response.body = JSON.stringify(this.value)
return request
}

View File

@@ -1,5 +1,6 @@
import AppClass from '../../lifecycle/AppClass.ts'
import {Route, RouteParameters} from './Route.ts'
import {RouteHandlers} from '../../unit/Routing.ts'
export default class ActivatedRoute extends AppClass {
public readonly params: RouteParameters
@@ -7,6 +8,7 @@ export default class ActivatedRoute extends AppClass {
constructor(
public readonly incoming: string,
public readonly route: Route,
public readonly handlers: RouteHandlers | undefined,
) {
super()
this.params = route.build_parameters(incoming)