Error response enhancements, CoreID auth client backend
This commit is contained in:
@@ -2,7 +2,7 @@ import {HTTPKernel} from '../HTTPKernel'
|
||||
import {Request} from '../../lifecycle/Request'
|
||||
import {ActivatedRoute} from '../../routing/ActivatedRoute'
|
||||
import {http} from '../../response/HTTPErrorResponseFactory'
|
||||
import {HTTPStatus} from '../../../util'
|
||||
import {HTTPStatus, withErrorContext} from '../../../util'
|
||||
import {AbstractResolvedRouteHandlerHTTPModule} from './AbstractResolvedRouteHandlerHTTPModule'
|
||||
|
||||
/**
|
||||
@@ -23,11 +23,15 @@ export class ExecuteResolvedRouteHandlerHTTPModule extends AbstractResolvedRoute
|
||||
throw new Error('Attempted to call route handler without resolved parameters.')
|
||||
}
|
||||
|
||||
const result = await route.handler
|
||||
.tap(handler => handler(...params))
|
||||
.apply(request)
|
||||
await withErrorContext(async () => {
|
||||
const result = await route.handler
|
||||
.tap(handler => handler(...params))
|
||||
.apply(request)
|
||||
|
||||
await this.applyResponseObject(result, request)
|
||||
await this.applyResponseObject(result, request)
|
||||
}, {
|
||||
route,
|
||||
})
|
||||
} else {
|
||||
await http(HTTPStatus.NOT_FOUND).write(request)
|
||||
request.response.blockingWriteback(true)
|
||||
|
||||
@@ -4,7 +4,7 @@ import {Request} from '../../lifecycle/Request'
|
||||
import {ActivatedRoute} from '../../routing/ActivatedRoute'
|
||||
import {ResponseObject} from '../../routing/Route'
|
||||
import {AbstractResolvedRouteHandlerHTTPModule} from './AbstractResolvedRouteHandlerHTTPModule'
|
||||
import {collect, isLeft, unleft, unright} from '../../../util'
|
||||
import {collect, isLeft, unleft, unright, withErrorContext} from '../../../util'
|
||||
|
||||
/**
|
||||
* HTTP Kernel module that executes the preflight handlers for the route.
|
||||
@@ -22,11 +22,13 @@ export class ExecuteResolvedRoutePreflightHTTPModule extends AbstractResolvedRou
|
||||
const preflight = route.preflight
|
||||
|
||||
for ( const handler of preflight ) {
|
||||
const result: ResponseObject = await handler(request)
|
||||
if ( typeof result !== 'undefined' ) {
|
||||
await this.applyResponseObject(result, request)
|
||||
request.response.blockingWriteback(true)
|
||||
}
|
||||
await withErrorContext(async () => {
|
||||
const result: ResponseObject = await handler(request)
|
||||
if ( typeof result !== 'undefined' ) {
|
||||
await this.applyResponseObject(result, request)
|
||||
request.response.blockingWriteback(true)
|
||||
}
|
||||
}, { handler })
|
||||
}
|
||||
|
||||
const parameters = route.parameters
|
||||
|
||||
@@ -195,6 +195,10 @@ export class Response {
|
||||
*/
|
||||
public async write(data: string | Buffer | Uint8Array | Readable): Promise<void> {
|
||||
return new Promise<void>((res, rej) => {
|
||||
if ( this.responseEnded ) {
|
||||
throw new ErrorWithContext('Tried to write to Response after lifecycle ended.')
|
||||
}
|
||||
|
||||
if ( !this.sentHeaders ) {
|
||||
this.sendHeaders()
|
||||
}
|
||||
|
||||
@@ -72,9 +72,12 @@ export class ErrorResponseFactory extends ResponseFactory {
|
||||
}
|
||||
}
|
||||
|
||||
const suggestion = this.getSuggestion()
|
||||
|
||||
let str = `
|
||||
<b>Sorry, an unexpected error occurred while processing your request.</b>
|
||||
<br>
|
||||
${suggestion ? '<br><b>Suggestion:</b> ' + suggestion + '<br>' : ''}
|
||||
<pre><code>
|
||||
Name: ${thrownError.name}
|
||||
Message: ${thrownError.message}
|
||||
@@ -88,7 +91,7 @@ Stack trace:
|
||||
str += `
|
||||
<pre><code>
|
||||
Context:
|
||||
${Object.keys(context).map(key => ` - ${key} : ${context[key]}`)
|
||||
${Object.keys(context).map(key => ` - ${key} : ${JSON.stringify(context[key]).replace(/\n/g, '<br>')}`)
|
||||
.join('\n')}
|
||||
</code></pre>
|
||||
`
|
||||
@@ -100,4 +103,12 @@ ${Object.keys(context).map(key => ` - ${key} : ${context[key]}`)
|
||||
protected buildJSON(thrownError: Error): string {
|
||||
return JSON.stringify(api.error(thrownError))
|
||||
}
|
||||
|
||||
protected getSuggestion(): string {
|
||||
if ( this.thrownError.message.startsWith('No such dependency is registered with this container: class SecurityContext') ) {
|
||||
return 'It looks like this route relies on the security framework. Is the route you are accessing inside a middleware (e.g. SessionAuthMiddleware)?'
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ import {Request} from '../lifecycle/Request'
|
||||
* Helper function to create a new RedirectResponseFactory to the given destination.
|
||||
* @param destination
|
||||
*/
|
||||
export function redirect(destination: string): RedirectResponseFactory {
|
||||
return new RedirectResponseFactory(destination)
|
||||
export function redirect(destination: string|URL): RedirectResponseFactory {
|
||||
return new RedirectResponseFactory(destination instanceof URL ? destination.toString() : destination)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -72,12 +72,16 @@ export class Route<TReturn extends ResponseObject, THandlerParams extends unknow
|
||||
for ( const group of stack ) {
|
||||
route.prepend(group.prefix)
|
||||
group.getPreflight()
|
||||
.each(def => route.preflight.prepend(def))
|
||||
.each(def => route.preflight.prepend(
|
||||
request => request.make<Middleware>(def, request).apply(),
|
||||
))
|
||||
}
|
||||
|
||||
for ( const group of this.compiledGroupStack ) {
|
||||
group.getPostflight()
|
||||
.each(def => route.postflight.push(def))
|
||||
.each(def => route.postflight.push(
|
||||
request => request.make<Middleware>(def, request).apply(),
|
||||
))
|
||||
}
|
||||
|
||||
// Add the global pre- and post- middleware
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import {Collection, ErrorWithContext} from '../../util'
|
||||
import {AppClass} from '../../lifecycle/AppClass'
|
||||
import {ResolvedRouteHandler} from './Route'
|
||||
import {Container} from '../../di'
|
||||
import {Container, Instantiable} from '../../di'
|
||||
import {Logging} from '../../service/Logging'
|
||||
import {Middleware} from './Middleware'
|
||||
|
||||
/**
|
||||
* Class that defines a group of Routes in the application, with a prefix.
|
||||
*/
|
||||
export class RouteGroup extends AppClass {
|
||||
protected preflight: Collection<ResolvedRouteHandler> = new Collection<ResolvedRouteHandler>()
|
||||
protected preflight: Collection<Instantiable<Middleware>> = new Collection()
|
||||
|
||||
protected postflight: Collection<ResolvedRouteHandler> = new Collection<ResolvedRouteHandler>()
|
||||
protected postflight: Collection<Instantiable<Middleware>> = new Collection()
|
||||
|
||||
/**
|
||||
* The current set of nested groups. This is used when compiling route groups.
|
||||
@@ -87,22 +87,22 @@ export class RouteGroup extends AppClass {
|
||||
}
|
||||
|
||||
/** Register the given middleware to be applied before all routes in this group. */
|
||||
pre(middleware: ResolvedRouteHandler): this {
|
||||
pre(middleware: Instantiable<Middleware>): this {
|
||||
this.preflight.push(middleware)
|
||||
return this
|
||||
}
|
||||
|
||||
/** Register the given middleware to be applied after all routes in this group. */
|
||||
post(middleware: ResolvedRouteHandler): this {
|
||||
post(middleware: Instantiable<Middleware>): this {
|
||||
this.postflight.push(middleware)
|
||||
return this
|
||||
}
|
||||
|
||||
getPreflight(): Collection<ResolvedRouteHandler> {
|
||||
getPreflight(): Collection<Instantiable<Middleware>> {
|
||||
return this.preflight
|
||||
}
|
||||
|
||||
getPostflight(): Collection<ResolvedRouteHandler> {
|
||||
getPostflight(): Collection<Instantiable<Middleware>> {
|
||||
return this.postflight
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Injectable, Inject} from '../../di'
|
||||
import {ErrorWithContext} from '../../util'
|
||||
import {ErrorWithContext, Safe} from '../../util'
|
||||
import {Request} from '../lifecycle/Request'
|
||||
|
||||
/**
|
||||
@@ -60,4 +60,9 @@ export abstract class Session {
|
||||
|
||||
/** Remove a key from the session data. */
|
||||
public abstract forget(key: string): void
|
||||
|
||||
/** Load a key from the session as a Safe value. */
|
||||
public safe(key: string): Safe {
|
||||
return new Safe(this.get(key))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user