Integrate file upload form middleware into controllers
This commit is contained in:
parent
892cf71917
commit
4b8a3b307d
@ -64,8 +64,10 @@ export default abstract class Application {
|
|||||||
|
|
||||||
// Init express
|
// Init express
|
||||||
const app = express();
|
const app = express();
|
||||||
const router = express.Router({});
|
const mainRouter = express.Router();
|
||||||
app.use(router);
|
const fileUploadFormRouter = express.Router();
|
||||||
|
app.use(fileUploadFormRouter);
|
||||||
|
app.use(mainRouter);
|
||||||
|
|
||||||
// Error handler
|
// Error handler
|
||||||
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
|
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
|
||||||
@ -135,11 +137,11 @@ export default abstract class Application {
|
|||||||
|
|
||||||
// Start all components
|
// Start all components
|
||||||
for (const component of this.components) {
|
for (const component of this.components) {
|
||||||
await component.start(app, router);
|
await component.start(app, mainRouter);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routes
|
// Routes
|
||||||
this.routes(router);
|
this.routes(mainRouter, fileUploadFormRouter);
|
||||||
|
|
||||||
this.ready = true;
|
this.ready = true;
|
||||||
}
|
}
|
||||||
@ -175,24 +177,24 @@ export default abstract class Application {
|
|||||||
Logger.info(`${this.constructor.name} v${this.version} - bye`);
|
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) {
|
for (const controller of this.controllers) {
|
||||||
if (controller.hasGlobalHandlers()) {
|
if (controller.hasGlobalHandlers()) {
|
||||||
controller.setupGlobalHandlers(rootRouter);
|
controller.setupGlobalHandlers(mainRootRouter);
|
||||||
|
|
||||||
Logger.info(`Registered global middlewares for controller ${controller.constructor.name}`);
|
Logger.info(`Registered global middlewares for controller ${controller.constructor.name}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const controller of this.controllers) {
|
for (const controller of this.controllers) {
|
||||||
const router = express.Router();
|
const {mainRouter, fileUploadFormRouter} = controller.setupRoutes();
|
||||||
controller.setupRoutes(router);
|
mainRootRouter.use(controller.getRoutesPrefix(), mainRouter);
|
||||||
rootRouter.use(controller.getRoutesPrefix(), router);
|
rootFileUploadFormRouter.use(controller.getRoutesPrefix(), fileUploadFormRouter);
|
||||||
|
|
||||||
Logger.info(`> Registered routes for controller ${controller.constructor.name}`);
|
Logger.info(`> Registered routes for controller ${controller.constructor.name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
rootRouter.use((req: Request) => {
|
mainRootRouter.use((req: Request) => {
|
||||||
throw new NotFoundHttpError('page', req.originalUrl);
|
throw new NotFoundHttpError('page', req.originalUrl);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import {RequestHandler, Router} from "express";
|
import express, {IRouter, RequestHandler, Router} from "express";
|
||||||
import {PathParams} from "express-serve-static-core";
|
import {PathParams} from "express-serve-static-core";
|
||||||
import config from "config";
|
import config from "config";
|
||||||
import Logger from "./Logger";
|
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 {
|
export default abstract class Controller {
|
||||||
private static readonly routes: { [p: string]: string } = {};
|
private static readonly routes: { [p: string]: string } = {};
|
||||||
@ -33,7 +34,8 @@ export default abstract class Controller {
|
|||||||
return `${absolute ? config.get<string>('public_url') : ''}${path}`;
|
return `${absolute ? config.get<string>('public_url') : ''}${path}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private router?: Router;
|
private readonly router: Router = express.Router();
|
||||||
|
private readonly fileUploadFormRouter: Router = express.Router();
|
||||||
|
|
||||||
public getGlobalHandlers(): RequestHandler[] {
|
public getGlobalHandlers(): RequestHandler[] {
|
||||||
return [];
|
return [];
|
||||||
@ -55,56 +57,59 @@ export default abstract class Controller {
|
|||||||
|
|
||||||
public abstract routes(): void;
|
public abstract routes(): void;
|
||||||
|
|
||||||
public setupRoutes(router: Router): void {
|
public setupRoutes(): {
|
||||||
this.router = router;
|
mainRouter: Router,
|
||||||
|
fileUploadFormRouter: Router
|
||||||
|
} {
|
||||||
this.routes();
|
this.routes();
|
||||||
|
return {
|
||||||
|
mainRouter: this.router,
|
||||||
|
fileUploadFormRouter: this.fileUploadFormRouter,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected use(handler: RequestHandler) {
|
protected use(handler: RequestHandler) {
|
||||||
this.router?.use(this.wrap(handler));
|
this.router.use(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected get(path: PathParams, handler: RequestHandler, routeName?: string, ...middlewares: RequestHandler[]) {
|
protected get(path: PathParams, handler: RequestHandler, routeName?: string, ...middlewares: (RequestHandler | FileUploadMiddleware)[]) {
|
||||||
this.registerRoutes(path, handler, routeName);
|
this.handle('get', path, handler, routeName, ...middlewares);
|
||||||
for (const middleware of middlewares) {
|
|
||||||
this.router?.get(path, this.wrap(middleware));
|
|
||||||
}
|
|
||||||
this.router?.get(path, this.wrap(handler));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected post(path: PathParams, handler: RequestHandler, routeName?: string, ...middlewares: RequestHandler[]) {
|
protected post(path: PathParams, handler: RequestHandler, routeName?: string, ...middlewares: (RequestHandler | FileUploadMiddleware)[]) {
|
||||||
this.registerRoutes(path, handler, routeName);
|
this.handle('post', path, handler, routeName, ...middlewares);
|
||||||
for (const middleware of middlewares) {
|
|
||||||
this.router?.post(path, this.wrap(middleware));
|
|
||||||
}
|
|
||||||
this.router?.post(path, this.wrap(handler));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected put(path: PathParams, handler: RequestHandler, routeName?: string, ...middlewares: RequestHandler[]) {
|
protected put(path: PathParams, handler: RequestHandler, routeName?: string, ...middlewares: (RequestHandler | FileUploadMiddleware)[]) {
|
||||||
this.registerRoutes(path, handler, routeName);
|
this.handle('put', path, handler, routeName, ...middlewares);
|
||||||
for (const middleware of middlewares) {
|
|
||||||
this.router?.put(path, this.wrap(middleware));
|
|
||||||
}
|
|
||||||
this.router?.put(path, this.wrap(handler));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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<keyof IRouter, 'stack' | 'param' | 'route' | 'use'>,
|
||||||
|
path: PathParams,
|
||||||
|
handler: RequestHandler,
|
||||||
|
routeName?: string,
|
||||||
|
...middlewares: (RequestHandler | FileUploadMiddleware)[]
|
||||||
|
): void {
|
||||||
this.registerRoutes(path, handler, routeName);
|
this.registerRoutes(path, handler, routeName);
|
||||||
for (const middleware of middlewares) {
|
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 {
|
private wrap(handler: RequestHandler): RequestHandler {
|
||||||
return (req, res, next) => {
|
return async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const promise = handler.call(this, req, res, next);
|
await handler.call(this, req, res, next);
|
||||||
if (promise instanceof Promise) {
|
|
||||||
promise.catch(e => {
|
|
||||||
next(e);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
next(e);
|
next(e);
|
||||||
}
|
}
|
||||||
@ -159,3 +164,30 @@ export default abstract class Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type RouteParams = { [p: string]: string } | string[] | string | number;
|
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<any>((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();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
11
src/FileUploadMiddleware.ts
Normal file
11
src/FileUploadMiddleware.ts
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -46,30 +46,3 @@ export default class ExpressAppComponent extends ApplicationComponent<void> {
|
|||||||
return this.server;
|
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<any>((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();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
Loading…
Reference in New Issue
Block a user