Mongoose 提供了强大的数据验证功能,可以在保存数据到数据库之前验证数据的完整性和正确性。验证可以在 Schema 层面定义,也可以自定义验证器。
内置验证器
1. 必填验证(required)
javascriptconst userSchema = new Schema({ name: { type: String, required: [true, 'Name is required'] }, email: { type: String, required: true } });
2. 类型验证(type)
javascriptconst userSchema = new Schema({ age: Number, isActive: Boolean, birthDate: Date });
3. 枚举验证(enum)
javascriptconst userSchema = new Schema({ status: { type: String, enum: ['active', 'inactive', 'pending'], enum: { values: ['active', 'inactive', 'pending'], message: '{VALUE} is not a valid status' } } });
4. 范围验证(min, max)
javascriptconst userSchema = new Schema({ age: { type: Number, min: [0, 'Age must be at least 0'], max: [120, 'Age cannot exceed 120'] }, score: { type: Number, min: 0, max: 100 } });
5. 长度验证(minlength, maxlength)
javascriptconst userSchema = new Schema({ username: { type: String, minlength: [3, 'Username must be at least 3 characters'], maxlength: [20, 'Username cannot exceed 20 characters'] } });
6. 正则表达式验证(match)
javascriptconst userSchema = new Schema({ email: { type: String, match: [/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/, 'Please fill a valid email address'] }, phone: { type: String, match: /^[0-9]{10}$/, message: 'Phone number must be 10 digits' } });
7. 唯一验证(unique)
javascriptconst userSchema = new Schema({ email: { type: String, unique: true, index: true } });
8. 默认值(default)
javascriptconst userSchema = new Schema({ status: { type: String, default: 'active' }, createdAt: { type: Date, default: Date.now } });
自定义验证器
单字段验证器
javascriptconst userSchema = new Schema({ password: { type: String, validate: { validator: function(v) { return v.length >= 8; }, message: 'Password must be at least 8 characters long' } } });
异步验证器
javascriptconst userSchema = new Schema({ email: { type: String, validate: { validator: async function(v) { const user = await this.constructor.findOne({ email: v }); return !user || user._id.toString() === this._id.toString(); }, message: 'Email already exists' } } });
多字段验证器
javascriptconst userSchema = new Schema({ password: String, confirmPassword: String }); userSchema.path('confirmPassword').validate(function(v) { return v === this.password; }, 'Passwords do not match');
验证时机
验证在以下时机自动触发:
save()- 保存文档时validate()- 显式调用验证时validateSync()- 同步验证时
javascriptconst user = new User({ name: '', age: -5 }); try { await user.save(); } catch (err) { console.log(err.errors.name.message); // "Name is required" console.log(err.errors.age.message); // "Age must be at least 0" }
跳过验证
在某些情况下,可以跳过验证:
javascript// 跳过验证保存 await user.save({ validateBeforeSave: false }); // 跳过验证更新 await User.findByIdAndUpdate(id, { age: 25 }, { runValidators: false });
验证错误处理
javascriptuserSchema.pre('validate', function(next) { if (this.password !== this.confirmPassword) { this.invalidate('confirmPassword', 'Passwords do not match'); } next(); }); // 捕获验证错误 try { await user.save(); } catch (err) { if (err.name === 'ValidationError') { Object.keys(err.errors).forEach(field => { console.log(`${field}: ${err.errors[field].message}`); }); } }
最佳实践
- 在 Schema 层面定义验证规则
- 提供清晰的错误消息
- 使用异步验证器检查唯一性
- 在前端和后端都进行验证
- 考虑性能影响,避免过于复杂的验证
- 使用自定义验证器处理业务逻辑
- 记录验证失败的情况