如何使用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,
);
main.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处理猫鼬错误的主要内容,如果未能解决你的问题,请参考以下文章