From 17fbbe9e7f1c5b37d7f91fa33e90bc1ef968a94c Mon Sep 17 00:00:00 2001 From: Arthur Melin Date: Fri, 29 May 2020 18:25:54 +0200 Subject: [PATCH] Add middleware guard to prevent multiple routes execution per request --- src/driver/express/ExpressDriver.ts | 24 ++++++++++++++++-------- src/driver/koa/KoaDriver.ts | 12 ++++++++++++ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/driver/express/ExpressDriver.ts b/src/driver/express/ExpressDriver.ts index c6ea6a6d..13a4975e 100644 --- a/src/driver/express/ExpressDriver.ts +++ b/src/driver/express/ExpressDriver.ts @@ -160,20 +160,28 @@ export class ExpressDriver extends BaseDriver { // prepare route and route handler function const route = ActionMetadata.appendBaseRoute(this.routePrefix, actionMetadata.fullRoute); const routeHandler = function routeHandler(request: any, response: any, next: Function) { - // Express calls the "get" route automatically when we call the "head" route: - // Reference: https://expressjs.com/en/4x/api.html#router.METHOD - // This causes a double action execution on our side, which results in an unhandled rejection, - // saying: "Can't set headers after they are sent". - // The following line skips action processing when the request method does not match the action method. - if (request.method.toLowerCase() !== actionMetadata.type) - return next(); - return executeCallback({request, response, next}); }; + // This ensures that a request is only processed once to prevent unhandled rejections saying + // "Can't set headers after they are sent" + // Some examples of reasons a request may cause multiple route calls: + // * Express calls the "get" route automatically when we call the "head" route: + // Reference: https://expressjs.com/en/4x/api.html#router.METHOD + // This causes a double execution on our side. + // * Multiple routes match the request (e.g. GET /users/me matches both @All(/users/me) and @Get(/users/:id)). + // The following middleware only starts an action processing if the request has not been processed before. + const routeGuard = function routeGuard(request: any, response: any, next: Function) { + if (!request.routingControllersStarted) { + request.routingControllersStarted = true; + return next(); + } + }; + // finally register action in express this.express[actionMetadata.type.toLowerCase()](...[ route, + routeGuard, ...beforeMiddlewares, ...defaultMiddlewares, routeHandler, diff --git a/src/driver/koa/KoaDriver.ts b/src/driver/koa/KoaDriver.ts index 4d8a1761..2531a195 100644 --- a/src/driver/koa/KoaDriver.ts +++ b/src/driver/koa/KoaDriver.ts @@ -132,9 +132,21 @@ export class KoaDriver extends BaseDriver { return executeCallback(options); }; + // This ensures that a request is only processed once. Multiple routes may match a request + // e.g. GET /users/me matches both @All(/users/me) and @Get(/users/:id)), only the first matching route should + // be called. + // The following middleware only starts an action processing if the request has not been processed before. + const routeGuard = (context: any, next: () => Promise) => { + if (!context.request.routingControllersStarted) { + context.request.routingControllersStarted = true; + return next(); + } + }; + // finally register action in koa this.router[actionMetadata.type.toLowerCase()](...[ route, + routeGuard, ...beforeMiddlewares, ...defaultMiddlewares, routeHandler,