From 4b8a3b307d5e85222d4af5237fd99f5515a0d75b Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Sat, 11 Jul 2020 11:08:57 +0200 Subject: [PATCH] Integrate file upload form middleware into controllers --- src/Application.ts | 22 +++--- src/Controller.ts | 102 +++++++++++++++++--------- src/FileUploadMiddleware.ts | 11 +++ src/components/ExpressAppComponent.ts | 27 ------- 4 files changed, 90 insertions(+), 72 deletions(-) create mode 100644 src/FileUploadMiddleware.ts diff --git a/src/Application.ts b/src/Application.ts index f448daf..2677958 100644 --- a/src/Application.ts +++ b/src/Application.ts @@ -64,8 +64,10 @@ export default abstract class Application { // Init express const app = express(); - const router = express.Router({}); - app.use(router); + const mainRouter = express.Router(); + const fileUploadFormRouter = express.Router(); + app.use(fileUploadFormRouter); + app.use(mainRouter); // Error handler app.use((err: any, req: Request, res: Response, next: NextFunction) => { @@ -135,11 +137,11 @@ export default abstract class Application { // Start all components for (const component of this.components) { - await component.start(app, router); + await component.start(app, mainRouter); } // Routes - this.routes(router); + this.routes(mainRouter, fileUploadFormRouter); this.ready = true; } @@ -175,24 +177,24 @@ export default abstract class Application { Logger.info(`${this.constructor.name} v${this.version} - bye`); } - private routes(rootRouter: Router) { + private routes(mainRootRouter: Router, rootFileUploadFormRouter: Router) { for (const controller of this.controllers) { if (controller.hasGlobalHandlers()) { - controller.setupGlobalHandlers(rootRouter); + controller.setupGlobalHandlers(mainRootRouter); Logger.info(`Registered global middlewares for controller ${controller.constructor.name}`); } } for (const controller of this.controllers) { - const router = express.Router(); - controller.setupRoutes(router); - rootRouter.use(controller.getRoutesPrefix(), router); + const {mainRouter, fileUploadFormRouter} = controller.setupRoutes(); + mainRootRouter.use(controller.getRoutesPrefix(), mainRouter); + rootFileUploadFormRouter.use(controller.getRoutesPrefix(), fileUploadFormRouter); Logger.info(`> Registered routes for controller ${controller.constructor.name}`); } - rootRouter.use((req: Request) => { + mainRootRouter.use((req: Request) => { throw new NotFoundHttpError('page', req.originalUrl); }); } diff --git a/src/Controller.ts b/src/Controller.ts index 605abd5..32ef104 100644 --- a/src/Controller.ts +++ b/src/Controller.ts @@ -1,8 +1,9 @@ -import {RequestHandler, Router} from "express"; +import express, {IRouter, RequestHandler, Router} from "express"; import {PathParams} from "express-serve-static-core"; import config from "config"; import Logger from "./Logger"; -import Validator, {ValidationBag} from "./db/Validator"; +import Validator, {FileError, ValidationBag} from "./db/Validator"; +import FileUploadMiddleware from "./FileUploadMiddleware"; export default abstract class Controller { private static readonly routes: { [p: string]: string } = {}; @@ -33,7 +34,8 @@ export default abstract class Controller { return `${absolute ? config.get('public_url') : ''}${path}`; } - private router?: Router; + private readonly router: Router = express.Router(); + private readonly fileUploadFormRouter: Router = express.Router(); public getGlobalHandlers(): RequestHandler[] { return []; @@ -55,56 +57,59 @@ export default abstract class Controller { public abstract routes(): void; - public setupRoutes(router: Router): void { - this.router = router; + public setupRoutes(): { + mainRouter: Router, + fileUploadFormRouter: Router + } { this.routes(); + return { + mainRouter: this.router, + fileUploadFormRouter: this.fileUploadFormRouter, + }; } protected use(handler: RequestHandler) { - this.router?.use(this.wrap(handler)); + this.router.use(handler); } - protected get(path: PathParams, handler: RequestHandler, routeName?: string, ...middlewares: RequestHandler[]) { - this.registerRoutes(path, handler, routeName); - for (const middleware of middlewares) { - this.router?.get(path, this.wrap(middleware)); - } - this.router?.get(path, this.wrap(handler)); + protected get(path: PathParams, handler: RequestHandler, routeName?: string, ...middlewares: (RequestHandler | FileUploadMiddleware)[]) { + this.handle('get', path, handler, routeName, ...middlewares); } - protected post(path: PathParams, handler: RequestHandler, routeName?: string, ...middlewares: RequestHandler[]) { - this.registerRoutes(path, handler, routeName); - for (const middleware of middlewares) { - this.router?.post(path, this.wrap(middleware)); - } - this.router?.post(path, this.wrap(handler)); + protected post(path: PathParams, handler: RequestHandler, routeName?: string, ...middlewares: (RequestHandler | FileUploadMiddleware)[]) { + this.handle('post', path, handler, routeName, ...middlewares); } - protected put(path: PathParams, handler: RequestHandler, routeName?: string, ...middlewares: RequestHandler[]) { - this.registerRoutes(path, handler, routeName); - for (const middleware of middlewares) { - this.router?.put(path, this.wrap(middleware)); - } - this.router?.put(path, this.wrap(handler)); + protected put(path: PathParams, handler: RequestHandler, routeName?: string, ...middlewares: (RequestHandler | FileUploadMiddleware)[]) { + this.handle('put', path, handler, routeName, ...middlewares); } - protected delete(path: PathParams, handler: RequestHandler, routeName?: string, ...middlewares: RequestHandler[]) { + protected delete(path: PathParams, handler: RequestHandler, routeName?: string, ...middlewares: (RequestHandler | FileUploadMiddleware)[]) { + this.handle('delete', path, handler, routeName, ...middlewares); + } + + private handle( + action: Exclude, + path: PathParams, + handler: RequestHandler, + routeName?: string, + ...middlewares: (RequestHandler | FileUploadMiddleware)[] + ): void { this.registerRoutes(path, handler, routeName); for (const middleware of middlewares) { - this.router?.delete(path, this.wrap(middleware)); + if (middleware instanceof FileUploadMiddleware) { + this.fileUploadFormRouter[action](path, this.wrap(FILE_UPLOAD_MIDDLEWARE(middleware))); + } else { + this.router[action](path, this.wrap(middleware)); + } } - this.router?.delete(path, this.wrap(handler)); + this.router[action](path, this.wrap(handler)); } private wrap(handler: RequestHandler): RequestHandler { - return (req, res, next) => { + return async (req, res, next) => { try { - const promise = handler.call(this, req, res, next); - if (promise instanceof Promise) { - promise.catch(e => { - next(e); - }); - } + await handler.call(this, req, res, next); } catch (e) { next(e); } @@ -158,4 +163,31 @@ export default abstract class Controller { } } -export type RouteParams = { [p: string]: string } | string[] | string | number; \ No newline at end of file +export type RouteParams = { [p: string]: string } | string[] | string | number; + +const FILE_UPLOAD_MIDDLEWARE: (fileUploadMiddleware: FileUploadMiddleware) => RequestHandler = (fileUploadMiddleware: FileUploadMiddleware) => { + return async (req, res, next) => { + const form = fileUploadMiddleware.formFactory(); + try { + await new Promise((resolve, reject) => { + form.parse(req, (err, fields, files) => { + if (err) { + reject(err); + return; + } + req.body = fields; + req.files = files; + resolve(); + }); + }); + } catch (e) { + const bag = new ValidationBag(); + const fileError = new FileError(e); + fileError.thingName = fileUploadMiddleware.defaultField; + bag.addMessage(fileError); + next(bag); + return; + } + next(); + }; +}; diff --git a/src/FileUploadMiddleware.ts b/src/FileUploadMiddleware.ts new file mode 100644 index 0000000..499fb25 --- /dev/null +++ b/src/FileUploadMiddleware.ts @@ -0,0 +1,11 @@ +import {IncomingForm} from "formidable"; + +export default class FileUploadMiddleware { + public readonly formFactory: () => IncomingForm; + public readonly defaultField: string; + + public constructor(formFactory: () => IncomingForm, defaultField: string) { + this.formFactory = formFactory; + this.defaultField = defaultField; + } +} \ No newline at end of file diff --git a/src/components/ExpressAppComponent.ts b/src/components/ExpressAppComponent.ts index 56b8946..91f4204 100644 --- a/src/components/ExpressAppComponent.ts +++ b/src/components/ExpressAppComponent.ts @@ -46,30 +46,3 @@ export default class ExpressAppComponent extends ApplicationComponent { return this.server; } } - -export const FILE_UPLOAD_MIDDLEWARE: (formFactory: () => IncomingForm, defaultField: string) => RequestHandler = (formFactory: () => IncomingForm, defaultField: string) => { - return async (req, res, next) => { - const form = formFactory(); - try { - await new Promise((resolve, reject) => { - form.parse(req, (err, fields, files) => { - if (err) { - reject(err); - return; - } - req.body = fields; - req.files = files; - resolve(); - }); - }); - } catch (e) { - const bag = new ValidationBag(); - const fileError = new FileError(e); - fileError.thingName = defaultField; - bag.addMessage(fileError); - next(bag); - return; - } - next(); - }; -};