NestJS中Mongoose不对嵌套数组执行校验的问题
Mongoose嵌套子文档校验失效问题解决
问题场景
我定义了如下嵌套用户Schema:
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; import * as mongoose from 'mongoose'; import { User } from 'src/users/schemas/user.schema'; @Schema({ _id: false }) export class NestedUser { @Prop({ required: true, type: mongoose.Schema.Types.ObjectId, ref: 'User' }) userId: User; @Prop({ required: true }) name: string; @Prop({ required: true }) email: string; @Prop({ required: false, default: '' }) imageUrl: string; } export const NestedUserSchema = SchemaFactory.createForClass(NestedUser);
同时定义了包含NestedUser数组的角色Schema:
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; import * as mongoose from 'mongoose'; import { NestedUser, NestedUserSchema } from './nested-user.schema'; import { NestedTemplate, NestedTemplateSchema } from './nested-template.schema'; export type RoleDocument = mongoose.HydratedDocument<Role>; @Schema() export class Role { @Prop({ required: true }) name: string; @Prop({ required: true, type: [NestedTemplateSchema], default: [] }) templates: NestedTemplate[]; @Prop({ required: true, type: [NestedUserSchema], default: [], }) users: NestedUser[]; } export const RoleSchema = SchemaFactory.createForClass(Role);
但插入包含空字段的嵌套用户数据时,子文档未经过校验就被成功插入:
{ "userId": "", "name": "", "imageUrl": "", "email": "" }
问题原因
required: true的局限性:Mongoose中required: true仅保证字段存在,对于字符串类型,空字符串''属于合法值,不会被拦截。- ObjectId类型未做有效性校验:空字符串不是有效的MongoDB ObjectId,但Mongoose默认不会自动校验ObjectId格式,仅校验字段是否存在。
- 子文档数组校验未完全触发:父Schema中数组类型的子文档,默认可能未强制触发每个子项的完整校验规则。
解决方案
1. 增强NestedUser的字段校验
给每个必填字段添加更严格的校验规则,确保内容合法:
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; import * as mongoose from 'mongoose'; import { User } from 'src/users/schemas/user.schema'; @Schema({ _id: false }) export class NestedUser { @Prop({ required: true, type: mongoose.Schema.Types.ObjectId, ref: 'User', // 校验是否为有效ObjectId validate: { validator: (val: string) => mongoose.Types.ObjectId.isValid(val), message: 'userId必须是有效的MongoDB ObjectId' } }) userId: User; @Prop({ required: true, // 确保名称非空 minlength: 1, message: 'name不能为空' }) name: string; @Prop({ required: true, // 确保邮箱非空且格式合法 minlength: 1, match: /^\S+@\S+\.\S+$/, message: '邮箱格式不正确或不能为空' }) email: string; @Prop({ required: false, default: '' }) imageUrl: string; } export const NestedUserSchema = SchemaFactory.createForClass(NestedUser);
2. 开启父Schema的严格模式并增强数组校验
给Role Schema开启严格模式,同时给users数组添加校验,确保每个子项都符合规则:
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; import * as mongoose from 'mongoose'; import { NestedUser, NestedUserSchema } from './nested-user.schema'; import { NestedTemplate, NestedTemplateSchema } from './nested-template.schema'; export type RoleDocument = mongoose.HydratedDocument<Role>; // 开启严格模式,禁止插入Schema未定义的字段,增强校验严谨性 @Schema({ strict: 'throw' }) export class Role { @Prop({ required: true }) name: string; @Prop({ required: true, type: [NestedTemplateSchema], default: [] }) templates: NestedTemplate[]; @Prop({ required: true, type: [NestedUserSchema], default: [], // 校验数组中每个用户子文档的合法性 validate: { validator: (users: NestedUser[]) => { return users.every(user => { return mongoose.Types.ObjectId.isValid(user.userId) && user.name.trim() !== '' && /^\S+@\S+\.\S+$/.test(user.email); }); }, message: '用户列表中存在无效的子文档' } }) users: NestedUser[]; } export const RoleSchema = SchemaFactory.createForClass(Role);
3. 插入/更新时强制触发校验
在执行数据库操作时,添加runValidators: true选项,强制Mongoose触发所有校验规则:
// 创建Role文档时开启校验 const role = new RoleModel(roleData); await role.save({ runValidators: true }); // 更新时开启校验 await RoleModel.findByIdAndUpdate(roleId, updateData, { runValidators: true, new: true });
内容的提问来源于stack exchange,提问作者Abd al rahman shebani
相关产品推荐
相关产品推荐

