Home > Mobile >  Cannot catch BadRequestException thrown by FilesInterceptor
Cannot catch BadRequestException thrown by FilesInterceptor

Time:09-20

I am trying to set up a logic to save uploaded files using Multer. To do this, I follow the Nestjs tutorial and use the "FilesInterceptor" interceptor.

controller file :

import {
    Controller,
    FileValidator,
    ParseFilePipe,
    Post,
    UploadedFiles,
    UseInterceptors
} from '@nestjs/common';
import { FilesInterceptor } from '@nestjs/platform-express';
import { Public } from 'src/auth/decorators/public.decorator';
import { MimeTypeValidationPipe } from './pipes/mimetype.validation.pipe';

const ACCEPTED_FILE_MIMETYPES = ["image/jpeg", "image/jpg", "image/png"]
const validators: FileValidator[] = [
    new MimeTypeValidationPipe({ accepted: ACCEPTED_FILE_MIMETYPES })
];

@Controller('uploads')
export class UploadsController {

    @Public()
    @Post("/")
    @UseInterceptors(FilesInterceptor("files"))
    public async create(
        @UploadedFiles(new ParseFilePipe({ validators })) 
        files: Express.Multer.File[]
    ){
        files[0].originalname
        const filenames = files.map(({ originalname }) => originalname)
        return { filenames };
    }
}

However, when I test the behavior of the server when the number of uploaded files exceeds the limit, the server returns me an error 500 (As if the error was not handled). I then try to catch it by using an ExcepetionFilter like this one:

@Catch()
class TestFilter implements ExceptionFilter {
    catch(exception: any, host: ArgumentsHost) {
        console.debug(exception)
        if(exception instanceof HttpException) console.debug("This is an HTTP Exception !");
        else console.debug("This is NOT an HTTP Exception");
        const response = host.switchToHttp().getResponse<Response>();
        return response.status(500).json({statusCode: 500 , message: "ERROR" });
    }
}

And i get the following output :

BadRequestException: Too many files
    at transformException (~/development/Nest/nestapi/node_modules/@nestjs/platform-express/multer/multer/multer.utils.js:19:20)
    at ~/development/Nest/nestapi/node_modules/@nestjs/platform-express/multer/interceptors/files.interceptor.js:18:73
    at ~/development/Nest/nestapi/node_modules/@nestjs/platform-express/node_modules/multer/lib/make-middleware.js:53:37
    at AsyncResource.runInAsyncScope (node:async_hooks:202:9)
    at listener (~/development/Nest/nestapi/node_modules/@nestjs/platform-express/node_modules/on-finished/index.js:170:15)
    at onFinish (~/development/Nest/nestapi/node_modules/@nestjs/platform-express/node_modules/on-finished/index.js:101:5)
    at callback (~/development/Nest/nestapi/node_modules/@nestjs/platform-express/node_modules/ee-first/index.js:55:10)
    at IncomingMessage.onevent (~/development/Nest/nestapi/node_modules/@nestjs/platform-express/node_modules/ee-first/index.js:93:5)
    at IncomingMessage.emit (node:events:539:35)
    at endReadableNT (node:internal/streams/readable:1345:12) {
  response: { statusCode: 400, message: 'Too many files', error: 'Bad Request' },
  status: 400
}
This is NOT an HTTP Exception

The filter indicates that it is NOT an HTTPException. However, while digging in the FilesInterceptor.ts code I notice that the caught errors are handled by a small utility function "transformException" which is supposed to transform the Multer error into an HttpException (depending on the error code returned by Multer)

multer.utils.ts file (from nest repo)

import {
  BadRequestException,
  HttpException,
  PayloadTooLargeException,
} from '@nestjs/common';
import { multerExceptions } from './multer.constants';

export function transformException(error: Error | undefined) {
  if (!error || error instanceof HttpException) {
    return error;
  }
  switch (error.message) {
    case multerExceptions.LIMIT_FILE_SIZE:
      return new PayloadTooLargeException(error.message);
    case multerExceptions.LIMIT_FILE_COUNT:
    case multerExceptions.LIMIT_FIELD_KEY:
    case multerExceptions.LIMIT_FIELD_VALUE:
    case multerExceptions.LIMIT_FIELD_COUNT:
    case multerExceptions.LIMIT_UNEXPECTED_FILE:
    case multerExceptions.LIMIT_PART_COUNT:
      return new BadRequestException(error.message);
  }
  return error;
}

I don't understand why my filter (and NestJS' unhandledExceptionFilter) can't detect this exception, since for me it is supposed to be an instance of HttpException.

Can you help me?

Best regards

CodePudding user response:

Sorry!

I think the problem is with me.

The LTS version (1.4.4-lts.1) of multer is buggy. So I decided to downgrade to 1.4.4 (version in which the bug in question does not occur). But to do so, I had to downgrade the nested dependency manually by doing npm install [email protected] in the node_modules/@nest/platform-express directory.

But that's when nestjs starts to format my errors badly.

The funny thing is that going back (npm install [email protected] to the node_modules/@nest/platform-express directory), it doesn't solve the problem (Errors are still badly handled) and I have to delete the node_modules/@nest/platform-express folder and reinstall the package from the root of the project to get everything back in order (But with the LTS version bug, of course).

It's weird.

CodePudding user response:

You probably have 2 copies of @nestjs/common being included in your project. The code that creates the error is using one copy, and your exception filter is using the other copy. When your code is checking instanceof, it's checking to see if the exception is an instance of HttpException from it's copy of @nestjs/common, but it's not, it's an instance of HttpException from the other copy of @nestjs/common. This is known as "multiple realms" (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof#instanceof_and_multiple_realms).

The way to fix this is to make sure you only have 1 copy of @nestjs/common in your project. Often, the reason you have 2 is because you have 2 package.json files with different version specs that they call for (e.g. "@nestjs/common": "^8.0.0" in one package.json, and "@nestjs/common": "^9.0.0" in another). You may need to use e.g. the overrides key to force a dependency to use the same version that you use elsewhere.

Hope that helps!

  • Related