如何在Mongoose Schema的Prop中自定义唯一约束不满足时的错误信息?
解决NestJS+Mongoose中唯一约束冲突的自定义错误信息问题
你遇到的问题根源在于:Mongoose的unique属性是用来创建MongoDB层面的唯一索引,而非Schema验证规则,所以直接在@Prop里加message不会生效。当违反唯一约束时,MongoDB会抛出E11000重复键错误,默认Nest会返回500状态码,需要手动捕获并处理这个错误。
以下是两种可行的解决方案:
方案一:全局异常过滤器统一处理
创建全局异常过滤器,专门捕获MongoDB的重复键错误,返回自定义响应:
- 创建
mongo-exception.filter.ts:
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) { const ctx = host.switchToHttp(); const response = ctx.getResponse<Response>(); if (exception.code === 11000) { // 解析重复的字段名 const field = Object.keys(exception.keyPattern)[0]; response.status(HttpStatus.BAD_REQUEST).json({ statusCode: HttpStatus.BAD_REQUEST, message: `${field}已被占用,请更换后重试`, error: 'Bad Request' }); } else { // 其他Mongo错误按默认逻辑处理 response.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ statusCode: HttpStatus.INTERNAL_SERVER_ERROR, message: '服务器错误', error: 'Internal Server Error' }); } } }
- 在
main.ts中注册全局过滤器:
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { MongoExceptionFilter } from './mongo-exception.filter'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalFilters(new MongoExceptionFilter()); await app.listen(3000); } bootstrap();
方案二:在Service层捕获错误并抛出自定义异常
在创建用户的Service方法中,手动捕获MongoError,抛出Nest内置的业务异常:
import { Injectable, BadRequestException } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import { User } from './user.schema'; import { MongoError } from 'mongodb'; @Injectable() export class UserService { constructor(@InjectModel(User.name) private userModel: Model<User>) {} async create(userDto: any) { try { const createdUser = new this.userModel(userDto); return await createdUser.save(); } catch (error) { if (error instanceof MongoError && error.code === 11000) { throw new BadRequestException('邮箱已存在,请更换其他邮箱'); } throw error; // 其他错误继续向上抛出 } } }
补充说明
- 建议保留MongoDB的唯一索引:即使在Service层做了前置检查,并发场景下依然可能出现重复插入,DB层面的索引是最终的保障。
- 如果需要更细粒度的Schema验证,可以自定义验证器,但无法替代唯一索引的作用:
@Prop({ required: true, validate: { validator: async function(value) { const user = await this.constructor.findOne({ email: value }); return !user; }, message: '邮箱已存在' } }) email: string;
内容的提问来源于stack exchange,提问作者DogX2
相关产品推荐
相关产品推荐

