You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Mongoose Schema的Prop中自定义唯一约束不满足时的错误信息?

解决NestJS+Mongoose中唯一约束冲突的自定义错误信息问题

你遇到的问题根源在于:Mongoose的unique属性是用来创建MongoDB层面的唯一索引,而非Schema验证规则,所以直接在@Prop里加message不会生效。当违反唯一约束时,MongoDB会抛出E11000重复键错误,默认Nest会返回500状态码,需要手动捕获并处理这个错误。

以下是两种可行的解决方案:

方案一:全局异常过滤器统一处理

创建全局异常过滤器,专门捕获MongoDB的重复键错误,返回自定义响应:

  1. 创建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'
      });
    }
  }
}
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 10:32:48