Validation
在我们进入验证语法的细节之前,请记住以下规则:
- Validation是在SchemaType定义
- Validation是中间件的内部组件
- Validation发生在document试图在默认值应用之后保存的时候。
- Validation不在未定义的值运行,唯一例外是必要的验证器。
- Validation是异步递归的,当你调用Model#save,子文档验证也会执行。如果发生错误,Model#save回调会接收它。
- 验证支持完全定制
内置验证器
mongoose有几个内置验证器。
上面的每一个验证器链接提供关于如何使用它们和定制错误信息。
var schema = new mongoose.Schema({ name:{ type:'String', required: true, maxlength: 10, match: /^a/ }, date: { type: Date, default: Date.now }, age:{ type:'Number', min:18, //年龄最小18 max:120 //年龄最大120 }, city:{ type:'String', enum:['北京','上海'] //只能是北京、上海人 }, });
自定义验证器
如果内置验证器不够,validation能够完全可以满足你的需求。
自定义验证通过函数传递声明。你能在SchemaType#validate() API 文档找到如何实现它的详细说明。
var userSchema = new Schema({ phone: { type: String, validate: { validator: function(v) { return /d{3}-d{3}-d{4}/.test(v); }, message: '{VALUE} is not a valid phone number!' } } }); var User = mongoose.model('user', userSchema); var u = new User(); u.phone = '555.0123'; // Prints "ValidationError: 555.0123 is not a valid phone number!" console.log(u.validateSync().toString()); u.phone = '201-555-0123'; // Prints undefined - validation succeeded! console.log(u.validateSync());
错误验证
Validation失败后返回的Errors包含一个持有实际ValidatorErrors的errors对象。每个ValidatorError有kind、path、value和message属性。
var toySchema = new Schema({ color: String, name: String }); var Toy = mongoose.model('Toy', toySchema); Toy.schema.path('color').validate(function (value) { return /blue|green|white|red|orange|periwinkle/i.test(value); }, 'Invalid color'); var toy = new Toy({ color: 'grease'}); toy.save(function (err) { // err is our ValidationError object // err.errors.color is a ValidatorError object console.log(err.errors.color.message) // prints 'Validator "Invalid color" failed for path color with value `grease`' console.log(String(err.errors.color)) // prints 'Validator "Invalid color" failed for path color with value `grease`' console.log(err.errors.color.kind) // prints "Invalid color" console.log(err.errors.color.path) // prints "color" console.log(err.errors.color.value) // prints "grease" console.log(err.name) // prints "ValidationError" console.log(err.message) // prints "Validation failed" });
经过验证错误,该文件将有相同的错误属性:
toy.errors.color.message === err.errors.color.message
同步验证
Validation默认是异步的。mongoose用独立的 process.nextTick() 调用估计每个独立path ,这确保了如果有许多路径要验证, validation不会阻塞event loop。可是,mongoose的内置验证器都是同步的,通常它比同步验证更方便。
mongoose document也有validateSync() 方法,如果有错误它返回ValidationError否则返回falsy。注意,validatesync()只执行同步的验证器。自定义异步验证器不会执行。
var toySchema = new Schema({ color: String, name: String }); var Toy = mongoose.model('Toy', toySchema); Toy.schema.path('color').validate(function (value) { return /blue|green|white|red|orange|periwinkle/i.test(value); }, 'Invalid color'); var toy = new Toy({ color: 'grease'}); // `error` is a ValidationError analogous to the one from `validate()` var error = toy.validateSync(); // prints 'Validator "Invalid color" failed for path color with value `grease`' console.log(error.errors.color.message);
修改验证
mongoose也支持update() 和 findOneAndUpdate() 操作的验证。在Mongoose 4.x,update验证器默认是关闭的 - 你需要指定runValidators 选项。
var toySchema = new Schema({ color: String, name: String }); var Toy = mongoose.model('Toy', toySchema); Toy.schema.path('color').validate(function (value) { return /blue|green|white|red|orange|periwinkle/i.test(value); }, 'Invalid color'); Toy.update({}, { color: 'bacon' }, { runValidators: true }, function (err) { console.log(err.errors.color.message); // prints 'Validator "Invalid color" failed for path color with value `bacon`' });
修改验证器和document验证器直接有两个个区别。在上面的颜色验证器函数,当使用document验证器时它引用被验证的document。可是,当运行修改验证器时,被修改的document可能不在服务器的内存,因此在默认情况下这个值未定义。然而,你可以设置上下文选项为'query'来使它引用基础query。
Toy.schema.path('color').validate(function (value) { this.schema; // refers to the query's schema if you set the `context` option return /blue|green|white|red|orange|periwinkle/i.test(value); }, 'Invalid color'); var options = { runValidators: true, context: 'query' }; Toy.update({}, { color: 'bacon' }, options, function (err) { console.log(err.errors.color.message); // prints 'Validator "Invalid color" failed for path color with value `bacon`' });
另一个主要差别是修改验证器只运行在修改中指定的path。例如,在下面的例子,因为在修改操作中没有指定'name',更新validation会成功。
var toySchema = new Schema({ color: String, name: { type: String, required: true } }); var Toy = mongoose.model('Toy', toySchema); Toy.update({}, { color: 'blue' }, { runValidators: true }, function(err) { // Operation succeeds despite the fact that 'name' is not specified });
使用修改验证器时,当你 $unset 键的时候,必要的验证器才会失败。
var unsetOp = { $unset: { name: 1 } }; Toy.update({}, unsetOp, { runValidators: true }, function(err) { // Operation fails because 'name' is required });
最后值得注意的一个细节:修改验证器只在$set和$unset操作运行。例如,不管number的值是多少,下面的修改都会成功。
var testSchema = new Schema({ number: { type: Number, max: 0 }, }); var Test = mongoose.model('Test', testSchema); Test.update({}, { $inc: { number: 1 } }, { runValidators: true }, function (err) { // There will never be a validation error here });