如何使用nestjs处理猫鼬错误

Posted

技术标签:

【中文标题】如何使用nestjs处理猫鼬错误【英文标题】:How to handle mongoose error with nestjs 【发布时间】:2018-11-24 14:46:53 【问题描述】:

我按照https://docs.nestjs.com/techniques/mongodb的例子

问题是当出现猫鼬验证错误时(例如,我有一个带有必填字段但未提供的架构):

来自 games.service.ts:

  async create(createGameDto: CreateGameDto): Promise<IGame> 
    const createdGame = new this.gameModel(createGameDto);
    return await createdGame.save();
  

save() 函数返回一个 Promise。

现在我在 game.controller.ts 中有这个

  @Post()
  async create(@Body() createGameDto: CreateGameDto) 
    this.gamesService.create(createGameDto);
  

处理错误然后返回具有不同 http 状态以及可能是 json 文本的响应的最佳方法是什么? 你通常会抛出一个HttpException,但从哪里来?如果我在 promise 中使用 .catch() 处理错误,我将无法做到这一点。

(刚开始使用nestjs框架)

【问题讨论】:

【参考方案1】:

我在我的应用程序中所做的是使用Exception Filters (https://docs.nestjs.com/exception-filters) 和try/catch

  async create(createGameDto: CreateGameDto): Promise<IGame> 
    try 
      const createdGame = new this.gameModel(createGameDto);
      return await createdGame.save();
     catch (e) 
       // the e here would be MongoError
       throw new InternalServerException(e.message);
    
  

【讨论】:

我仍然得到 UnhandledPromiseRejectionWarning: using the try catch block, it doesn't work. 你确定它在你的“创建”功能中吗? github.com/nartc/nest-demo/blob/master/NestDemo.Server/src/user/… 看看我的仓库【参考方案2】:

首先,您忘记在控制器内的 create 方法中添加 return。这是一个常见的、非常容易误导的错误,我犯了 1000 次并且花了我几个小时来调试。

捕获异常:

您可以尝试使用 @Catch 捕获 MongoError。

对于我的项目,我正在执行以下操作:

import  ArgumentsHost, Catch, ConflictException, ExceptionFilter  from '@nestjs/common';
import  MongoError  from 'mongodb';

@Catch(MongoError)
export class MongoExceptionFilter implements ExceptionFilter 
  catch(exception: MongoError, host: ArgumentsHost) 
    switch (exception.code) 
      case 11000:
        // duplicate exception
        // do whatever you want here, for instance send error to client
    
  

然后您可以在控制器中像这样使用它(甚至可以将其用作全局/类范围过滤器):

import  MongoExceptionFilter  from '<path>/mongo-exception.filter';

@Get()
@UseFilters(MongoExceptionFilter)
async findAll(): Promise<User[]> 
  return this.userService.findAll();

(重复异常在 findAll() 调用中没有意义,但你明白了)。

此外,我强烈建议使用类验证器,如下所述:https://docs.nestjs.com/pipes

【讨论】:

对我来说这个解决方案不起作用。我有完全相同的设置,除了控制器异步方法是对在 mongodb 上执行 .save() 的服务的调用。自定义 MongoExceptionFilter 不会触发。我猜MongoError 不会被“捕获”,因为当我将 Catch 留空时,它会触发... "@nestjs/core": "^5.4.0", "@nestjs/mongoose": "^5.2 .2", "猫鼬": "^5.4.7", @MarnixHarderwijk 我也遇到了同样的情况。否决这个答案,因为它不起作用。【参考方案3】:

使用 try/catch

async getUser(id: string, validateUser ?: boolean): Promise<Users> 
    try 
      const user = await this.userModel.findById(id).exec();
      if(!user && validateUser) 
        throw new UnauthorizedException();
      else if(!user) 
        throw new HttpException(`Not found this id: $id`, HttpStatus.NOT_FOUND)
      
      return user;
     catch (err) 
      throw new HttpException(`Callback getUser $err.message`, HttpStatus.BAD_REQUEST);
    

【讨论】:

这不是最好的方法,因为你必须在你的应用程序中编写 try/catch 块,在使用 Nestjs 时不建议这样做,因为它有自己的异常系统。 【参考方案4】:

你可以在mongoose中使用Error并添加到AllExceptionFilter

请参阅 NestJS 文档以获取 exception-filters

import 
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  HttpStatus,
  InternalServerErrorException
 from "@nestjs/common";

@Catch()
export class AllExceptionsFilter implements ExceptionFilter 
  catch(exception: InternalServerErrorException, host: ArgumentsHost) 
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const request = ctx.getRequest();

    const status =
      exception instanceof HttpException
        ? exception.getStatus()
        : HttpStatus.INTERNAL_SERVER_ERROR;

    /**
     * @description Exception json response
     * @param message
     */
    const responseMessage = (type, message) => 
      response.status(status).json(
        statusCode: status,
        path: request.url,
        errorType: type,
        errorMessage: message
      );
    ;

    // Throw an exceptions for either
    // MongoError, ValidationError, TypeError, CastError and Error
    if (exception.message.error) 
      responseMessage("Error", exception.message.error);
     else 
      responseMessage(exception.name, exception.message);
    
  

您可以像这样将其添加到 main.ts 中,但这实际上取决于您的用例。您可以在 Nest.js documentation 中查看。

async function bootstrap() 

  const app = await NestFactory.create(AppModule);

  app.useGlobalFilters(new AllExceptionsFilter());

  await app.listen(3000);

bootstrap();

希望对你有帮助。

【讨论】:

【参考方案5】:

我做了一些研究,发现这个工作正常。如下创建一个 Mongo 异常过滤器

import  ExceptionFilter, Catch, ArgumentsHost, HttpStatus  from "@nestjs/common";
import  MongoError  from 'mongodb';
import  Response  from 'express';

@Catch(MongoError)
export class MongoExceptionFilter implements ExceptionFilter 

    catch(exception: MongoError, host: ArgumentsHost) 
        switch (exception.code) 
            case 11000:
                const ctx = host.switchToHttp();
                const response = ctx.getResponse<Response>();
                response.statusCode = HttpStatus.FORBIDDEN;
                response
                    .json(
                        statusCode: HttpStatus.FORBIDDEN,
                        timestamp: new Date().toISOString(),
                        message: 'You are already registered'
                    );
        
    


并且不要忘记定义控制器方法如下:

@UseFilters(MongoExceptionFilter)
  @Post('signup')
  @HttpCode(HttpStatus.OK)
  async createUser(@Body() createUserDto: CreateUserDto) 
    await this.userService.create(createUserDto);
  

希望这对某人有所帮助。干杯!

【讨论】:

【参考方案6】:

今天搞定

validation-error.filter.ts:

import  ArgumentsHost, Catch, RpcExceptionFilter  from '@nestjs/common';
import  Error  from 'mongoose';
import ValidationError = Error.ValidationError;

@Catch(ValidationError)
export class ValidationErrorFilter implements RpcExceptionFilter 

  catch(exception: ValidationError, host: ArgumentsHost): any 

    const ctx = host.switchToHttp(),
      response = ctx.getResponse();

    return response.status(400).json(
      statusCode: 400,
      createdBy: 'ValidationErrorFilter',
      errors: exception.errors,
    );
  

ma​​in.ts:

import  NestFactory  from '@nestjs/core';
import  AppModule  from './app.module';
import  ValidationErrorFilter  from './validation-error.filter';

async function bootstrap() 
  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new ValidationErrorFilter());
  await app.listen(process.env.PORT || 3000);

bootstrap();

结果:


  "statusCode": 400,
  "createdBy": "ValidationErrorFilter",
  "errors": 
    "dob": 
      "properties": 
        "message": "Path `dob` is required.",
        "type": "required",
        "path": "dob"
      ,
      "kind": "required",
      "path": "dob"
    ,
    "password": 
      "properties": 
        "message": "Path `password` is required.",
        "type": "required",
        "path": "password"
      ,
      "kind": "required",
      "path": "password"
    
  

【讨论】:

这行得通,谢谢! import Error from 'mongoose'; 是我所缺少的。【参考方案7】:

我在这里找到了解决方案,我使用的是两者的结合来捕获不同的错误

import  ArgumentsHost, Catch, ExceptionFilter, RpcExceptionFilter  from '@nestjs/common';
import  Error  from 'mongoose';
import  IDTOError  from '../errors/bad-request-exception.error';
import ValidationError = Error.ValidationError;
import  MongoError  from 'mongodb';


@Catch(MongoError)
export class MongoExceptionFilter implements ExceptionFilter 
  catch(exception: MongoError, host: ArgumentsHost) 
    // switch (exception.code) 
    //   case 11000:
    //   default: console.log(exception,'ALERT ERROR CATCHED');
    //     // duplicate exception
    //     // do whatever you want here, for instance send error to client


    //     /** MAIGOD */
    // 
    const ctx = host.switchToHttp(),
      response = ctx.getResponse();

    return response.status(400).json(<IDTOError>
      statusCode: 400,
      createdBy: 'ValidationErrorFilter, Schema or Model definition',
      errors: exception,
    );

  


@Catch(ValidationError)
export class ValidationErrorFilter implements RpcExceptionFilter 

  catch(exception: ValidationError, host: ArgumentsHost): any 

    const ctx = host.switchToHttp(),
      response = ctx.getResponse();

    return response.status(400).json(<IDTOError>
      statusCode: 400,
      createdBy: 'ValidationErrorFilter, Schema or Model definition',
      errors: exception.errors,
    );
  

【讨论】:

【参考方案8】:

我正在使用 Moongose,但此处或其他问题中的解决方案均不适合我;我按照文档示例做了这个,它确实对我有用。

src\filters\mongo-exception.filter.ts

import  ArgumentsHost, Catch, ExceptionFilter, HttpStatus  from '@nestjs/common';

import * as MongooseError from 'mongoose/lib/error'; // I couldn't see the error class is being exported from Mongoose

@Catch(MongooseError)
export class MongoExceptionFilter implements ExceptionFilter 
  catch(exception: MongooseError, host: ArgumentsHost) 
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    // const request = ctx.getRequest();

    let error;

    switch (exception.name) 
      case 'DocumentNotFoundError': 
        error = 
          statusCode: HttpStatus.NOT_FOUND,
          message: "Not Found"
        
        break;
      
      // case 'MongooseError':  break;  // general Mongoose error
      // case 'CastError':  break; 
      // case 'DisconnectedError':  break; 
      // case 'DivergentArrayError':  break; 
      // case 'MissingSchemaError':  break; 
      // case 'ValidatorError':  break; 
      // case 'ValidationError':  break; 
      // case 'ObjectExpectedError':  break; 
      // case 'ObjectParameterError':  break; 
      // case 'OverwriteModelError':  break; 
      // case 'ParallelSaveError':  break; 
      // case 'StrictModeError':  break; 
      // case 'VersionError':  break; 
      default: 
        error = 
          statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
          message: "Internal Error"
        
        break;
      
    

    response.status(error.statusCode).json(error);
  

src\main.ts

import  MongoExceptionFilter  from './filters/mongo-exception.filter';

async function bootstrap() 
  // .......

  app.useGlobalFilters(new MongoExceptionFilter); // Use Mongo exception filter

  await app.listen(3000);

bootstrap();

【讨论】:

【参考方案9】:

我正在使用带有记录器的全局异常过滤器:(WINSTON),它可以捕获几乎所有错误,包括 MongoDB 错误,不知道它是否符合标准但对我有用,我关注了Nest Js Doc 一些我的自定义需求!

import 
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
  HttpStatus,
  Inject,
  LoggerService,
 from '@nestjs/common';
import  Response  from 'express';
import  WINSTON_MODULE_NEST_PROVIDER  from 'nest-winston';

@Catch()
export class HttpExceptionFilter implements ExceptionFilter 
  constructor(
    @Inject(WINSTON_MODULE_NEST_PROVIDER)
    private readonly logger: LoggerService,
  ) 
  catch(exp: any, host: ArgumentsHost) 
    console.log(exp);
    this.logger.error(exp);
    const context = host.switchToHttp();
    const response = context.getResponse<Response>();
    const request = context.getRequest<Request>();

    const hasKey = Object.keys(exp).length > 0 && exp.hasOwnProperty('response') ? true : false;
    const isHttpInstance = exp instanceof HttpException ? true : false;

    const validErrors = hasKey && Array.isArray(exp.response.message) ? exp.response.message : [];
    const statusCode = isHttpInstance ? exp.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;
    const type = hasKey && exp.response.type ? exp.response.type : 'some_thing_went_error';
    const message = isHttpInstance ? exp.message : 'Oops Something went wrong!';
    const error = hasKey ? exp.response.error : exp;

    response.status(statusCode).json(
      message,
      type,
      validationErrors: validErrors,
      statusCode,
      error,
      timestamp: new Date().toISOString(),
      path: request.url,
    );
  

这将是 MongoDB 错误的响应:

    
    "message": "Oops Something went wrong!",
    "type": "some_thing_went_error",
    "validationErrors": [],
    "statusCode": 500,
    "error": 
        "errors": 
            "subRegion": 
                "name": "ValidatorError",
                "message": "Path `subRegion` is required.",
                "properties": 
                    "message": "Path `subRegion` is required.",
                    "type": "required",
                    "path": "subRegion"
                ,
                "kind": "required",
                "path": "subRegion"
            
        ,
        "_message": "ServiceRequest validation failed",
        "name": "ValidationError",
        "message": "ServiceRequest validation failed: subRegion: Path `subRegion` is required."
    ,
    "timestamp": "2022-02-08T07:43:35.962Z",
    "path": "/v1/service-request"

这将是正常错误时的响应:


    "message": "Bad Request Exception",
    "type": "some_thing_went_error",
    "validationErrors": [
        "isEmergency must be one of the following values: true, false",
        "isEmergency must be a string",
        "isEmergency should not be empty"
    ],
    "statusCode": 400,
    "error": "Bad Request",
    "timestamp": "2022-02-08T07:47:25.183Z",
    "path": "/v1/service-request"

如果您希望 type 是自定义类型,可以从应用程序中抛出,您可以使用以下代码

    throw new HttpException(
      
        status: HttpStatus.FORBIDDEN,
        type: 'otp_verification_error',
        message: 'Please verify your mobile number to complete the signUp process!',
      ,
      HttpStatus.FORBIDDEN,
    );

【讨论】:

以上是关于如何使用nestjs处理猫鼬错误的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 NestJS 命名法为猫鼬写下嵌套模式 [关闭]

使用@nestjs/mongoose 时如何在文档界面中定义静态猫鼬方法?

NestJS如何在单元测试中创建新的猫鼬模型?

使用猫鼬和nestJS自动增加字段

使用 NestJS 测试猫鼬模型

如何使用打字稿定义猫鼬模式参考字段?