- 英文原版
- 关于中文文档
- 快速上手
- 模式(Schemas)
- 模式类型(SchemaTypes)
- 连接(Connections)
- 模型(Models)
- 文档 (Documents)
- 子文档(Subdocuments)
- 查询 (queries)
- 验证 (validation)
- 中间件 (middleware)
- 填充 (Populate)
- 鉴别器 (Discriminators)
- 插件
- AWS Lambda
- API 文档
- Schema
- Connection
- Document
- Model
- Query
- Aggregate
- SchemaType
- VirtualType
- Error
- Version Compatibility
- FAQ
[
验证 ](#验证)
如果你要使用验证,请注意一下几点:
- 验证定义于 SchemaType
- 验证是一个中间件。它默认作为 pre('save')` 钩子注册在 schema 上
- 你可以使用
doc.validate(callback)或doc.validateSync()手动验证 - 验证器不对未定义的值进行验证,唯一例外是
required验证器 - 验证是异步递归的。当你调用 Model#save,子文档验证也会执行,出错的话 Model#save 回调会接收错误
- 验证是可定制的
var schema = new Schema({name: {type: String,required: true}});var Cat = db.model('Cat', schema);// This cat has no name :(var cat = new Cat();cat.save(function(error) {assert.equal(error.errors['name'].message,'Path `name` is required.');error = cat.validateSync();assert.equal(error.errors['name'].message,'Path `name` is required.');});
[
内建 Validators ](#内建-validators)
Mongoose 有一些内建验证器。
- 所有 SchemaTypes 都有内建 required 验证器。required 验证器使用
checkRequired()函数 判定这个值是否满足 required 验证器 - Numbers 有 min 和 max 验证器.
- Strings 有 enum、 match、 maxlength 和 minlength 验证器
上面的链接提供了使用和错误处理相关的详细信息
var breakfastSchema = new Schema({eggs: {type: Number,min: [6, 'Too few eggs'],max: 12},bacon: {type: Number,required: [true, 'Why no bacon?']},drink: {type: String,enum: ['Coffee', 'Tea'],required: function() {return this.bacon > 3;}}});var Breakfast = db.model('Breakfast', breakfastSchema);var badBreakfast = new Breakfast({eggs: 2,bacon: 0,drink: 'Milk'});var error = badBreakfast.validateSync();assert.equal(error.errors['eggs'].message,'Too few eggs');assert.ok(!error.errors['bacon']);assert.equal(error.errors['drink'].message,'`Milk` is not a valid enum value for path `drink`.');badBreakfast.bacon = 5;badBreakfast.drink = null;error = badBreakfast.validateSync();assert.equal(error.errors['drink'].message, 'Path `drink` is required.');badBreakfast.bacon = null;error = badBreakfast.validateSync();assert.equal(error.errors['bacon'].message, 'Why no bacon?');
[
unique 不是验证器
](#unique-不是验证器)
初学者常见的 unique 选项 不是验证器。这是构建 MongoDB unique indexes 的辅助函数。 详见 FAQ。
var uniqueUsernameSchema = new Schema({username: {type: String,unique: true}});var U1 = db.model('U1', uniqueUsernameSchema);var U2 = db.model('U2', uniqueUsernameSchema);var dup = [{ username: 'Val' }, { username: 'Val' }];U1.create(dup, function(error) {// Race condition! This may save successfully, depending on whether// MongoDB built the index before writing the 2 docs.});// Need to wait for the index to finish building before saving,// otherwise unique constraints may be violated.U2.once('index', function(error) {assert.ifError(error);U2.create(dup, function(error) {// Will error, but will *not* be a mongoose validation error, it will be// a duplicate key error.assert.ok(error);assert.ok(!error.errors);assert.ok(error.message.indexOf('duplicate key error') !== -1);});});// There's also a promise-based equivalent to the event emitter API.// The `init()` function is idempotent and returns a promise that// will resolve once indexes are done building;U2.init().then(function() {U2.create(dup, function(error) {// Will error, but will *not* be a mongoose validation error, it will be// a duplicate key error.assert.ok(error);assert.ok(!error.errors);assert.ok(error.message.indexOf('duplicate key error') !== -1);});});
[
自定义验证器 ](#自定义验证器)
如果内建检验器不够用了,你可以定义满足自己需要的检验器
自定义检验器通过传入一个检验函数来定义,更多细节请看 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!'},required: [true, 'User phone number required']}});var User = db.model('user', userSchema);var user = new User();var error;user.phone = '555.0123';error = user.validateSync();assert.equal(error.errors['phone'].message,'555.0123 is not a valid phone number!');user.phone = '';error = user.validateSync();assert.equal(error.errors['phone'].message,'User phone number required');user.phone = '201-555-0123';// Validation succeeds! Phone number is defined// and fits `DDD-DDD-DDDD`error = user.validateSync();assert.equal(error, null);
[
异步自定义验证器 ](#异步自定义验证器)
自定义检验器可以是异步的。如果检验函数 返回 promise (像 async 函数), mongoose 将会等待该 promise 完成。 如果你更喜欢使用回调函数,设置 isAsync 选项, mongoose 会将回调函数作为验证函数的第二个参数。
var userSchema = new Schema({name: {type: String,// You can also make a validator async by returning a promise. If you// return a promise, do **not** specify the `isAsync` option.validate: function(v) {return new Promise(function(resolve, reject) {setTimeout(function() {resolve(false);}, 5);});}},phone: {type: String,validate: {isAsync: true,validator: function(v, cb) {setTimeout(function() {var phoneRegex = /\d{3}-\d{3}-\d{4}/;var msg = v + ' is not a valid phone number!';// 第一个参数是布尔值,代表验证结果// 第二个参数是报错信息cb(phoneRegex.test(v), msg);}, 5);},// 默认报错信息会被 `cb()` 第二个参数覆盖message: 'Default error message'},required: [true, 'User phone number required']}});var User = db.model('User', userSchema);var user = new User();var error;user.phone = '555.0123';user.name = 'test';user.validate(function(error) {assert.ok(error);assert.equal(error.errors['phone'].message,'555.0123 is not a valid phone number!');assert.equal(error.errors['name'].message,'Validator failed for path `name` with value `test`');});
[
验证错误 ](#验证错误)
验证失败返回的 err 包含一个 ValidatorError 对象。 每一个 ValidatorError 都有 kind、path、 value 和 message 属性。 ValidatorError 也可能有 reason 属性, 如果检验器抛出错误,这个属性会包含该错误原因。
var toySchema = new Schema({color: String,name: String});var validator = function(value) {return /red|white|gold/i.test(value);};toySchema.path('color').validate(validator,'Color `{VALUE}` not valid', 'Invalid color');toySchema.path('name').validate(function(v) {if (v !== 'Turbo Man') {throw new Error('Need to get a Turbo Man for Christmas');}return true;}, 'Name `{VALUE}` is not valid');var Toy = db.model('Toy', toySchema);var toy = new Toy({ color: 'Green', name: 'Power Ranger' });toy.save(function (err) {// `err` is a ValidationError object// `err.errors.color` is a ValidatorError objectassert.equal(err.errors.color.message, 'Color `Green` not valid');assert.equal(err.errors.color.kind, 'Invalid color');assert.equal(err.errors.color.path, 'color');assert.equal(err.errors.color.value, 'Green');// mongoose 5 新特性,如果验证器抛错,// mongoose 会使用该错误信息。如果验证器返回 `false`,// mongoose 会使用 'Name `Power Ranger` is not valid'。assert.equal(err.errors.name.message,'Need to get a Turbo Man for Christmas');assert.equal(err.errors.name.value, 'Power Ranger');// If your validator threw an error, the `reason` property will contain// the original error thrown, including the original stack trace.assert.equal(err.errors.name.reason.message,'Need to get a Turbo Man for Christmas');assert.equal(err.name, 'ValidationError');});
[
嵌套对象中的 Required 检验器 ](#嵌套对象中的-required-检验器)
定义嵌套对象的验证器需要特别注意。
var personSchema = new Schema({name: {first: String,last: String}});assert.throws(function() {// 这里会报错,因为 'name' 不是“完整成熟的路径”personSchema.path('name').required(true);}, /Cannot.*'required'/);// 要让嵌套对象 required,要使用单独的嵌套 schemavar nameSchema = new Schema({first: String,last: String});personSchema = new Schema({name: {type: nameSchema,required: true}});var Person = db.model('Person', personSchema);var person = new Person();var error = person.validateSync();assert.ok(error.errors['name']);
[
Update 验证器 ](#update-验证器)
上例中,你学习了 document 的验证。Mongoose 还支持验证 update() 和 findOneAndUpdate() 操作。 Update 验证器默认关闭,如需打开,请另外配置 runValidators。
注意:update 验证器默认关闭是因为里面有几个注意事项必须先了解。
var toySchema = new Schema({color: String,name: String});var Toy = db.model('Toys', toySchema);Toy.schema.path('color').validate(function (value) {return /blue|green|white|red|orange|periwinkle/i.test(value);}, 'Invalid color');var opts = { runValidators: true };Toy.update({}, { color: 'bacon' }, opts, function (err) {assert.equal(err.errors.color.message,'Invalid color');});
[
Update 验证器与 this
](#update-验证器与-this)
update 验证器和 document 验证器有诸多不同。 上面的颜色验证函数,this 指向验证中的 document 。 然而 update 验证器运行时,被更新文档不一定存在于服务器内存, 所以 this 值未定义。
var toySchema = new Schema({color: String,name: String});toySchema.path('color').validate(function(value) {// When running in `validate()` or `validateSync()`, the// validator can access the document using `this`.// Does **not** work with update validators.if (this.name.toLowerCase().indexOf('red') !== -1) {return value !== 'red';}return true;});var Toy = db.model('ActionFigure', toySchema);var toy = new Toy({ color: 'red', name: 'Red Power Ranger' });var error = toy.validateSync();assert.ok(error.errors['color']);var update = { color: 'red', name: 'Red Power Ranger' };var opts = { runValidators: true };Toy.update({}, update, opts, function(error) {// The update validator throws an error:// "TypeError: Cannot read property 'toLowerCase' of undefined",// because `this` is **not** the document being updated when using// update validatorsassert.ok(error);});
[
context 选项
](#context-选项)
context 选项允许你把 update 验证器的 this 设置为 query。
toySchema.path('color').validate(function(value) {// When running update validators with the `context` option set to// 'query', `this` refers to the query object.if (this.getUpdate().$set.name.toLowerCase().indexOf('red') !== -1) {return value === 'red';}return true;});var Toy = db.model('Figure', toySchema);var update = { color: 'blue', name: 'Red Power Ranger' };// Note the context optionvar opts = { runValidators: true, context: 'query' };Toy.update({}, update, opts, function(error) {assert.ok(error.errors['color']);});
[
Update 验证器字段路径 ](#update-验证器字段路径)
另一个关键不同点是 update 验证器只运行于更新的字段。 下例中,因为 'name' 在更新操作未被指定,所以此次更新操作成功。
使用 update 验证器的时候, required 验证器只会在你对某个字段显式使用 $unset 才会触发。
var kittenSchema = new Schema({name: { type: String, required: true },age: Number});var Kitten = db.model('Kitten', kittenSchema);var update = { color: 'blue' };var opts = { runValidators: true };Kitten.update({}, update, opts, function(err) {// 即使 'name' 没有指定也操作成功了});var unset = { $unset: { name: 1 } };Kitten.update({}, unset, opts, function(err) {// 'name' required, 操作失败assert.ok(err);assert.ok(err.errors['name']);});
[
Update 验证器只运行于指定字段路径 ](#update-验证器只运行于指定字段路径)
最后要注意的是:update 验证器只运行于下列更新操作:
$set$unset$push(>= 4.8.0)$addToSet(>= 4.8.0)$pull(>= 4.12.0)$pullAll(>= 4.12.0)
例如,以下 update 成功执行,不管 number 的值,因为 update 验证器 无视 $inc 。同样, $push、$addToSet、 $pull 和 $pullAll 验证器 不会对数组自身验证,只会对数组中的元素验证。
var testSchema = new Schema({number: { type: Number, max: 0 },arr: [{ message: { type: String, maxlength: 10 } }]});// Update 验证器不会作检查,所以你再仍然可以 `$push` 两个元素到数组// 只要他们的 `message` 没有超长testSchema.path('arr').validate(function(v) {return v.length < 2;});var Test = db.model('Test', testSchema);var update = { $inc: { number: 1 } };var opts = { runValidators: true };Test.update({}, update, opts, function(error) {// 这里不会报错update = { $push: [{ message: 'hello' }, { message: 'world' }] };Test.update({}, update, opts, function(error) {// 这里也不会报错});});
[
$push 和 $addToSet ](#$push-和-$addtoset)
4.8.0 新特性: update 验证器也运行于 $push 和 $addToSet
var testSchema = new Schema({numbers: [{ type: Number, max: 0 }],docs: [{name: { type: String, required: true }}]});var Test = db.model('TestPush', testSchema);var update = {$push: {numbers: 1,docs: { name: null }}};var opts = { runValidators: true };Test.update({}, update, opts, function(error) {assert.ok(error.errors['numbers']);assert.ok(error.errors['docs']);});
mongoose