HTTP errors!

This commit is contained in:
garrettmills 2020-07-30 09:30:43 -05:00
parent 194fa476ca
commit f1c4fbdb95
No known key found for this signature in database
GPG Key ID: 6ACD58D6ADACFC6E
9 changed files with 211 additions and 7 deletions

View File

@ -22,3 +22,8 @@ unit tests
integration tests integration tests
documentation & docs site documentation & docs site
events and observables? events and observables?
no matching route handler
request timeout
not handlers -> not implemented
method not allowed
error response handler figure out json

View File

@ -1,6 +1,6 @@
import Controller from '../../../lib/src/http/Controller.ts' import Controller from '../../../lib/src/http/Controller.ts'
import {Request} from '../../../lib/src/http/Request.ts' import {Request} from '../../../lib/src/http/Request.ts'
import {error, html, redirect} from '../../../lib/src/http/response/helpers.ts' import {error, html, http, redirect} from '../../../lib/src/http/response/helpers.ts'
export default class TestController extends Controller { export default class TestController extends Controller {
@ -9,10 +9,10 @@ export default class TestController extends Controller {
} }
get_user_home(request: Request) { get_user_home(request: Request) {
return redirect('/home') return http(401, 'You should not be here!')
} }
create_user_home(request: Request) { create_user_home(request: Request) {
return error('Not implemented!') return http(401)
} }
} }

168
lib/src/const/http.ts Normal file
View File

@ -0,0 +1,168 @@
export enum HTTPStatus {
http100 = 100,
http101 = 101,
http102 = 102,
http200 = 200,
http201 = 201,
http202 = 202,
http203 = 203,
http204 = 204,
http205 = 205,
http206 = 206,
http207 = 207,
http300 = 300,
http301 = 301,
http302 = 302,
http303 = 303,
http304 = 304,
http305 = 305,
http307 = 307,
http308 = 308,
http400 = 400,
http401 = 401,
http402 = 402,
http403 = 403,
http404 = 404,
http405 = 405,
http406 = 406,
http407 = 407,
http408 = 408,
http409 = 409,
http410 = 410,
http411 = 411,
http412 = 412,
http413 = 413,
http414 = 414,
http415 = 415,
http416 = 416,
http417 = 417,
http418 = 418,
http419 = 419,
http420 = 420,
http422 = 422,
http423 = 423,
http424 = 424,
http428 = 428,
http429 = 429,
http431 = 431,
http500 = 500,
http501 = 501,
http502 = 502,
http503 = 503,
http504 = 504,
http505 = 505,
http507 = 507,
http511 = 511,
CONTINUE = 100,
SWITCHING_PROTOCOLS = 101,
PROCESSING = 102,
OK = 200,
CREATED = 201,
ACCEPTED = 202,
NON_AUTHORITATIVE_INFORMATION = 203,
NO_CONTENT = 204,
RESET_CONTENT = 205,
PARTIAL_CONTENT = 206,
MULTI_STATUS = 207,
MULTIPLE_CHOICES = 300,
MOVED_PERMANENTLY = 301,
MOVED_TEMPORARILY = 302,
SEE_OTHER = 303,
NOT_MODIFIED = 304,
USE_PROXY = 305,
TEMPORARY_REDIRECT = 307,
PERMANENT_REDIRECT = 308,
BAD_REQUEST = 400,
UNAUTHORIZED = 401,
PAYMENT_REQUIRED = 402,
FORBIDDEN = 403,
NOT_FOUND = 404,
METHOD_NOT_ALLOWED = 405,
NOT_ACCEPTABLE = 406,
PROXY_AUTHENTICATION_REQUIRED = 407,
REQUEST_TIMEOUT = 408,
CONFLICT = 409,
GONE = 410,
LENGTH_REQUIRED = 411,
PRECONDITION_FAILED = 412,
REQUEST_TOO_LONG = 413,
REQUEST_URI_TOO_LONG = 414,
UNSUPPORTED_MEDIA_TYPE = 415,
REQUESTED_RANGE_NOT_SATISFIABLE = 416,
EXPECTATION_FAILED = 417,
IM_A_TEAPOT = 418,
INSUFFICIENT_SPACE_ON_RESOURCE = 419,
METHOD_FAILURE = 420,
UNPROCESSABLE_ENTITY = 422,
LOCKED = 423,
FAILED_DEPENDENCY = 424,
PRECONDITION_REQUIRED = 428,
TOO_MANY_REQUESTS = 429,
REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
INTERNAL_SERVER_ERROR = 500,
NOT_IMPLEMENTED = 501,
BAD_GATEWAY = 502,
SERVICE_UNAVAILABLE = 503,
GATEWAY_TIMEOUT = 504,
HTTP_VERSION_NOT_SUPPORTED = 505,
INSUFFICIENT_STORAGE = 507,
NETWORK_AUTHENTICATION_REQUIRED = 511,
}
export const Message = {
100: 'Continue',
101: 'Switching Protocols',
102: 'Processing',
200: 'OK',
201: 'Created',
202: 'Accepted',
203: 'Non Authoritative Information',
204: 'No Content',
205: 'Reset Content',
206: 'Partial Content',
207: 'Multi-Status',
300: 'Multiple Choices',
301: 'Moved Permanently',
302: 'Moved Temporarily',
303: 'See Other',
304: 'Not Modified',
305: 'Use Proxy',
307: 'Temporary Redirect',
308: 'Permanent Redirect',
400: 'Bad Request',
401: 'Unauthorized',
402: 'Payment Required',
403: 'Forbidden',
404: 'Not Found',
405: 'Method Not Allowed',
406: 'Not Acceptable',
407: 'Proxy Authentication Required',
408: 'Request Timeout',
409: 'Conflict',
410: 'Gone',
411: 'Length Required',
412: 'Precondition Failed',
413: 'Request Entity Too Large',
414: 'Request-URI Too Long',
415: 'Unsupported Media Type',
416: 'Request Range Not Satisfiable',
417: 'Expectation Failed',
418: 'I\'m a teapot',
419: 'Insufficient Space on Resource',
420: 'Method Failure',
422: 'Unprocessable Entity',
423: 'Locked',
424: 'Failed Dependency',
428: 'Precondition Required',
429: 'Too Many Requests',
431: 'Request Header Fields Too Large',
500: 'Server Error',
501: 'Not Implemented',
502: 'Bad Gateway',
503: 'Service Unavailable',
504: 'Gateway Timeout',
505: 'HTTP Version Not Supported',
507: 'Insufficient Storage',
511: 'Network Authentication Required',
}

View File

@ -0,0 +1,10 @@
import {HTTPStatus, Message} from '../const/http.ts'
export default class HTTPError extends Error {
constructor(
public readonly http_status: HTTPStatus,
public readonly http_message?: string
) {
super(`HTTP ${http_status}: ${http_message || Message[http_status]}`)
}
}

View File

@ -1,13 +1,14 @@
import ResponseFactory from './ResponseFactory.ts' import ResponseFactory from './ResponseFactory.ts'
import {Request} from '../Request.ts' import {Request} from '../Request.ts'
import * as api from '../../support/api.ts' import * as api from '../../support/api.ts'
import {HTTPStatus} from '../../const/http.ts'
export default class ErrorResponseFactory extends ResponseFactory { export default class ErrorResponseFactory extends ResponseFactory {
protected target_mode: 'json' | 'html' = 'html' protected target_mode: 'json' | 'html' = 'html'
constructor( constructor(
public readonly error: Error, public readonly error: Error,
status: number = 500, status: HTTPStatus = HTTPStatus.INTERNAL_SERVER_ERROR,
output: 'json' | 'html' = 'html', output: 'json' | 'html' = 'html',
) { ) {
super() super()

View File

@ -0,0 +1,11 @@
import ErrorResponseFactory from './ErrorResponseFactory.ts'
import HTTPError from '../../error/HTTPError.ts'
export default class HTTPErrorResponseFactory extends ErrorResponseFactory {
constructor(
public readonly error: HTTPError,
output: 'json' | 'html',
) {
super(error, error.http_status, output)
}
}

View File

@ -1,15 +1,16 @@
import AppClass from '../../lifecycle/AppClass.ts' import AppClass from '../../lifecycle/AppClass.ts'
import {Request} from '../Request.ts' import {Request} from '../Request.ts'
import {HTTPStatus} from '../../const/http.ts'
export default abstract class ResponseFactory extends AppClass { export default abstract class ResponseFactory extends AppClass {
protected target_status: number = 200 protected target_status: HTTPStatus = HTTPStatus.OK
public async write(request: Request): Promise<Request> { public async write(request: Request): Promise<Request> {
request.response.status = this.target_status request.response.status = this.target_status
return request return request
} }
public status(status: number): ResponseFactory { public status(status: HTTPStatus): ResponseFactory {
this.target_status = status this.target_status = status
return this return this
} }

View File

@ -1,8 +1,9 @@
import ResponseFactory from './ResponseFactory.ts' import ResponseFactory from './ResponseFactory.ts'
import {Request} from '../Request.ts' import {Request} from '../Request.ts'
import {HTTPStatus} from '../../const/http.ts'
export default class TemporaryRedirectResponseFactory extends ResponseFactory { export default class TemporaryRedirectResponseFactory extends ResponseFactory {
protected target_status: number = 302 protected target_status: HTTPStatus = HTTPStatus.TEMPORARY_REDIRECT
constructor( constructor(
public readonly destination: string, public readonly destination: string,

View File

@ -5,6 +5,9 @@ import ErrorResponseFactory from './ErrorResponseFactory.ts'
import {Rehydratable} from '../../support/Rehydratable.ts' import {Rehydratable} from '../../support/Rehydratable.ts'
import DehydratedStateResponseFactory from './DehydratedStateResponseFactory.ts' import DehydratedStateResponseFactory from './DehydratedStateResponseFactory.ts'
import TemporaryRedirectResponseFactory from './TemporaryRedirectResponseFactory.ts' import TemporaryRedirectResponseFactory from './TemporaryRedirectResponseFactory.ts'
import {HTTPStatus} from "../../const/http.ts";
import HTTPErrorResponseFactory from "./HTTPErrorResponseFactory.ts";
import HTTPError from "../../error/HTTPError.ts";
export function json(value: any): JSONResponseFactory { export function json(value: any): JSONResponseFactory {
return make(JSONResponseFactory, value) return make(JSONResponseFactory, value)
@ -26,3 +29,7 @@ export function dehydrate(value: Rehydratable): DehydratedStateResponseFactory {
export function redirect(destination: string): TemporaryRedirectResponseFactory { export function redirect(destination: string): TemporaryRedirectResponseFactory {
return make(TemporaryRedirectResponseFactory, destination) return make(TemporaryRedirectResponseFactory, destination)
} }
export function http(status: HTTPStatus, message?: string) {
return make(HTTPErrorResponseFactory, new HTTPError(status, message))
}