From 40716fbc9d1aa33b5e0f4156b2987098727b0f33 Mon Sep 17 00:00:00 2001 From: yidongw Date: Thu, 25 Jan 2024 20:37:12 +0800 Subject: [PATCH] CLS Context is lost after using multer middleware (#695) related issue: https://github.com/expressjs/multer/issues/814 Used the solution described in the above link to fix the issue Co-authored-by: Alan Wang --- src/middlewares/openapi.multipart.ts | 91 +++++++++++++++------------- 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/src/middlewares/openapi.multipart.ts b/src/middlewares/openapi.multipart.ts index 5871ccce..e868b300 100644 --- a/src/middlewares/openapi.multipart.ts +++ b/src/middlewares/openapi.multipart.ts @@ -20,51 +20,58 @@ export function multipart( ): OpenApiRequestHandler { const mult = multer(options.multerOpts); const Ajv = createRequestAjv(apiDoc, { ...options.ajvOpts }); - return (req, res, next) => { + return async (req, res, next) => { // TODO check that format: binary (for upload) else do not use multer.any() // use multer.none() if no binary parameters exist if (shouldHandle(Ajv, req)) { - mult.any()(req, res, (err) => { - if (err) { - next(error(req, err)); - } else { - // TODO: - // If a form parameter 'file' is defined to take file value, but the user provides a string value instead - // req.files will be empty and req.body.file will be populated with a string - // This will incorrectly PASS validation. - // Instead, we should return a 400 with an invalid type e.g. file expects a file, but found string. - // - // In order to support this, we likely need to inspect the schema directly to find the type. - // For example, if param with type: 'string', format: 'binary' is defined, we expect to see it in - // req.files. If it's not present we should throw a 400 - // - // This is a bit complex because the schema may be defined inline (easy) or via a $ref (complex) in which - // case we must follow the $ref to check the type. - - if (req.files) { - // to handle single and multiple file upload at the same time, let us this initialize this count variable - // for example { "files": 5 } - const count_by_fieldname = (req.files) - .map((file) => file.fieldname) - .reduce((acc, curr) => { - acc[curr] = (acc[curr] || 0) + 1; - return acc; - }, {}); - - // add file(s) to body - Object.entries(count_by_fieldname).forEach( - ([fieldname, count]: [string, number]) => { - // TODO maybe also check in the api doc if it is a single upload or multiple - const is_multiple = count > 1; - req.body[fieldname] = is_multiple - ? new Array(count).fill('') - : ''; - }, - ); - } - next(); - } - }); + try { + await new Promise((resolve, reject) => { + mult.any()(req, res, (err) => { + if (err) { + reject(error(req, err)); + } else { + // TODO: + // If a form parameter 'file' is defined to take file value, but the user provides a string value instead + // req.files will be empty and req.body.file will be populated with a string + // This will incorrectly PASS validation. + // Instead, we should return a 400 with an invalid type e.g. file expects a file, but found string. + // + // In order to support this, we likely need to inspect the schema directly to find the type. + // For example, if param with type: 'string', format: 'binary' is defined, we expect to see it in + // req.files. If it's not present we should throw a 400 + // + // This is a bit complex because the schema may be defined inline (easy) or via a $ref (complex) in which + // case we must follow the $ref to check the type. + + if (req.files) { + // to handle single and multiple file upload at the same time, let us this initialize this count variable + // for example { "files": 5 } + const count_by_fieldname = (req.files) + .map((file) => file.fieldname) + .reduce((acc, curr) => { + acc[curr] = (acc[curr] || 0) + 1; + return acc; + }, {}); + + // add file(s) to body + Object.entries(count_by_fieldname).forEach( + ([fieldname, count]: [string, number]) => { + // TODO maybe also check in the api doc if it is a single upload or multiple + const is_multiple = count > 1; + req.body[fieldname] = is_multiple + ? new Array(count).fill('') + : ''; + }, + ); + } + resolve(); + } + }); + }); + next() + } catch (error) { + next(error); + } } else { next(); }