Repository: nestjs/nest
Pull request #10880 (2023-01-13 17:17:51)

fix(express): Map missing multer error and map busboy errors
## PR Checklist
Please check if your PR fulfills the following requirements:

- [x] The commit message follows our guidelines: https://github.com/nestjs/nest/blob/master/CONTRIBUTING.md
- [x] Tests for the changes have been added (for bug fixes / features)
- [ ] Docs have been added / updated (for bug fixes / features)


## PR Type
What kind of change does this PR introduce?

- [x] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Other... Please describe:

## What is the current behavior?

Some malformed requests return as 500 instead of 400. This can be worked around by a custom exception filter, but it would be great for the MulterModule to handle this natively.

## What is the new behavior?

`multer` errors, as well as some raw `busboy/multipart` errors now return as 400 instead of 500.

## Does this PR introduce a breaking change?
- [ ] Yes
- [x] No

<!-- If this PR contains a breaking change, please describe the impact and migration path for existing applications below. -->


## Other information


Patch:
diff --git a/packages/platform-express/multer/multer/multer.constants.ts b/packages/platform-express/multer/multer/multer.constants.ts
index e3c7b4557..5a6f6aaa4 100644
--- a/packages/platform-express/multer/multer/multer.constants.ts
+++ b/packages/platform-express/multer/multer/multer.constants.ts
@@ -1,4 +1,5 @@
 export const multerExceptions = {
+  // from https://github.com/expressjs/multer/blob/master/lib/multer-error.js
   LIMIT_PART_COUNT: 'Too many parts',
   LIMIT_FILE_SIZE: 'File too large',
   LIMIT_FILE_COUNT: 'Too many files',
@@ -6,4 +7,13 @@ export const multerExceptions = {
   LIMIT_FIELD_VALUE: 'Field value too long',
   LIMIT_FIELD_COUNT: 'Too many fields',
   LIMIT_UNEXPECTED_FILE: 'Unexpected field',
+  MISSING_FIELD_NAME: 'Field name missing',
+};
+
+export const busboyExceptions = {
+  // from https://github.com/mscdex/busboy/blob/master/lib/types/multipart.js
+  MULTIPART_BOUNDARY_NOT_FOUND: 'Multipart: Boundary not found',
+  MULTIPART_MALFORMED_PART_HEADER: 'Malformed part header',
+  MULTIPART_UNEXPECTED_END_OF_FORM: 'Unexpected end of form',
+  MULTIPART_UNEXPECTED_END_OF_FILE: 'Unexpected end of file',
 };
diff --git a/packages/platform-express/multer/multer/multer.utils.ts b/packages/platform-express/multer/multer/multer.utils.ts
index 81e19739c..9df215832 100644
--- a/packages/platform-express/multer/multer/multer.utils.ts
+++ b/packages/platform-express/multer/multer/multer.utils.ts
@@ -3,7 +3,7 @@ import {
   HttpException,
   PayloadTooLargeException,
 } from '@nestjs/common';
-import { multerExceptions } from './multer.constants';
+import { multerExceptions, busboyExceptions } from './multer.constants';
 
 export function transformException(error: Error | undefined) {
   if (!error || error instanceof HttpException) {
@@ -18,7 +18,14 @@ export function transformException(error: Error | undefined) {
     case multerExceptions.LIMIT_FIELD_COUNT:
     case multerExceptions.LIMIT_UNEXPECTED_FILE:
     case multerExceptions.LIMIT_PART_COUNT:
+    case multerExceptions.MISSING_FIELD_NAME:
       return new BadRequestException(error.message);
+    case busboyExceptions.MULTIPART_BOUNDARY_NOT_FOUND:
+      return new BadRequestException(error.message);
+    case busboyExceptions.MULTIPART_MALFORMED_PART_HEADER:
+    case busboyExceptions.MULTIPART_UNEXPECTED_END_OF_FORM:
+    case busboyExceptions.MULTIPART_UNEXPECTED_END_OF_FILE:
+      return new BadRequestException(`Multipart: ${error.message}`);
   }
   return error;
 }


Test patch:
diff --git a/packages/platform-express/test/multer/multer/multer.utils.spec.ts b/packages/platform-express/test/multer/multer/multer.utils.spec.ts
index 7ad1eec90..f3745b936 100644
--- a/packages/platform-express/test/multer/multer/multer.utils.spec.ts
+++ b/packages/platform-express/test/multer/multer/multer.utils.spec.ts
@@ -4,18 +4,21 @@ import {
   PayloadTooLargeException,
 } from '@nestjs/common';
 import { expect } from 'chai';
-import { multerExceptions } from '../../../multer/multer/multer.constants';
+import {
+  multerExceptions,
+  busboyExceptions,
+} from '../../../multer/multer/multer.constants';
 import { transformException } from '../../../multer/multer/multer.utils';
 
 describe('transformException', () => {
   describe('if error does not exist', () => {
-    it('behave as identity', () => {
+    it('should behave as identity', () => {
       const err = undefined;
       expect(transformException(err)).to.be.eq(err);
     });
   });
   describe('if error is instance of HttpException', () => {
-    it('behave as identity', () => {
+    it('should behave as identity', () => {
       const err = new HttpException('response', 500);
       expect(transformException(err)).to.be.eq(err);
     });
@@ -37,5 +40,22 @@ describe('transformException', () => {
         );
       });
     });
+    describe('and is busboy/multipart exception', () => {
+      it('should return "BadRequestException"', () => {
+        const err = { message: busboyExceptions.MULTIPART_BOUNDARY_NOT_FOUND };
+        expect(transformException(err as any)).to.be.instanceof(
+          BadRequestException,
+        );
+      });
+
+      it('should return "BadRequestException"', () => {
+        const err = {
+          message: busboyExceptions.MULTIPART_UNEXPECTED_END_OF_FORM,
+        };
+        expect(transformException(err as any)).to.be.instanceof(
+          BadRequestException,
+        );
+      });
+    });
   });
 });