Bringing local and remote repository in line with one another and adding Github Actions to publish package
This commit is contained in:
parent
28db152b87
commit
74997cb676
8 changed files with 479 additions and 0 deletions
62
src/decorators/ErrorHandler.ts
Normal file
62
src/decorators/ErrorHandler.ts
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import { Request, Response, NextFunction } from 'express';
|
||||
|
||||
import { ErrorController } from '../controllers/ErrorController';
|
||||
|
||||
/**
|
||||
* Class decorator to create custom error handling for the app.
|
||||
*
|
||||
* That is, so that custom error pages can easily be created and used in the app.
|
||||
*
|
||||
* @example
|
||||
* The following example show how to use the Controller decorator to setup a path `/path` with a GET, POST, PUT and DELETE method.
|
||||
* ```ts
|
||||
* import { Request, Response, NextFunction } from 'express';
|
||||
*
|
||||
* import { ErrorController, ErrorHandler } from '@BridgemanAccessible/ba-web-framework';
|
||||
*
|
||||
* @ErrorHandler(404)
|
||||
* export class Custom404PageController extends ErrorController {
|
||||
* handle(error: unknown, req: Request, res: Response, next: NextFunction) {
|
||||
* // ...
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param errorCode The error code to handle. This is the status code that will be returned by the server.
|
||||
* @param description A human readable string to describe the error this controller handles. This is used for debugging purposes only and is not used in the app itself.
|
||||
*/
|
||||
export function ErrorHandler<T extends { new(...args: any): ErrorController }>(errorCode: number, description?: string) {
|
||||
// Technically the function we return here with the target parameter is the actual decorator itself but we wrap it in case we ever want to add parameters to the decorator
|
||||
return function(target: T){
|
||||
Reflect.defineMetadata('errorHandler', target, target);
|
||||
|
||||
if(typeof target.prototype.handlesError === 'undefined') {
|
||||
// If the handlesError property is not set, we set it to the description passed to the decorator
|
||||
target.prototype.handlesError = description || `${errorCode}`;
|
||||
}
|
||||
|
||||
// We extend the class that is decorated and override the setup method to automatically setup the routes
|
||||
return class extends target {
|
||||
async handle(error: unknown, req: Request, res: Response, next: NextFunction) {
|
||||
// Because the headers have already been sent, we cannot send a response again.
|
||||
// So we check if the headers have been sent and if so, we call the next middleware (default) in the error chain.
|
||||
if(res.headersSent) {
|
||||
return next(error);
|
||||
}
|
||||
|
||||
// Create an instance of the decorated (original) class
|
||||
const controller: ErrorController = new (Reflect.getMetadata('errorHandler', target))();
|
||||
|
||||
// We only want to call the handle method if the error code matches the one denoted.
|
||||
if(res.statusCode === errorCode) {
|
||||
// Call the handle method of the controller and return the result
|
||||
return await controller.handle.bind(controller)(error, req, res, next);
|
||||
}
|
||||
else {
|
||||
// If the error code does not match, we call the next middleware in the error chain
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue