lib/src/http/response/ErrorResponseFactory.ts

91 lines
2.7 KiB
TypeScript
Raw Normal View History

import {ResponseFactory} from "./ResponseFactory"
import {ErrorWithContext, HTTPStatus} from "@extollo/util"
import {Request} from "../lifecycle/Request";
import * as api from "./api"
export function error(
error: Error | string,
status: HTTPStatus = HTTPStatus.INTERNAL_SERVER_ERROR,
output: 'json' | 'html' | 'auto' = 'auto'
): ErrorResponseFactory {
if ( typeof error === 'string' ) error = new Error(error)
return new ErrorResponseFactory(error, status, output)
}
export class ErrorResponseFactory extends ResponseFactory {
protected targetMode: 'json' | 'html' | 'auto' = 'auto'
constructor(
public readonly error: Error,
status: HTTPStatus,
output: 'json' | 'html' | 'auto' = 'auto'
) {
super()
this.status(status)
this.mode(output)
}
public mode(output: 'json' | 'html' | 'auto'): ErrorResponseFactory {
this.targetMode = output
return this
}
public async write(request: Request) {
request = await super.write(request)
const wants = request.wants()
if ( this.targetMode === 'json' || (this.targetMode === 'auto' && wants === 'json') ) {
request.response.setHeader('Content-Type', 'application/json')
request.response.body = this.buildJSON(this.error)
} else if ( this.targetMode === 'html' || (this.targetMode === 'auto' && (wants === 'html' || wants === 'unknown')) ) {
request.response.setHeader('Content-Type', 'text/html')
request.response.body = this.buildHTML(this.error)
}
// FIXME XML support
return request
}
/**
* Build the HTML display for the given error.
* @param {Error} error
* @return string
*/
protected buildHTML(error: Error) {
let context: any
if ( error instanceof ErrorWithContext ) {
context = error.context
if ( error.originalError ) {
error = error.originalError
}
}
let str = `
<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>
`
if ( context && typeof context === 'object' ) {
str += `
<pre><code>
Context:
${Object.keys(context).map(key => ` - ${key} : ${context[key]}`)}
</code></pre>
`
}
return str
}
protected buildJSON(error: Error) {
return JSON.stringify(api.error(error))
}
}