Validation验证
Before we get into the specifics of validation syntax, please keep the following rules in mind:在我们进入验证语法的细节之前,请记住以下规则:
Validation is defined in the SchemaType验证是在SchemaType中定义的Validation is middleware.验证是中间件。Mongoose registers validation as a默认情况下,Mongoose将验证注册为每个模式上的pre('save')
hook on every schema by default.pre('save')
挂钩。Validation always runs as the first验证始终作为第一个pre('save')
hook.pre('save')
挂钩运行。This means that validation doesn't run on any changes you make in这意味着验证不会在pre('save')
hooks.pre('save')
挂钩中所做的任何更改上运行。You can disable automatic validation before save by setting the您可以通过设置validateBeforeSave
optionvalidateBeforeSave
选项来禁用保存前的自动验证You can manually run validation using您可以使用doc.validate()
ordoc.validateSync()
doc.validate()
或doc.validateSync()
手动运行验证You can manually mark a field as invalid (causing validation to fail) by using doc.invalidate(...)您可以使用doc.invalidate(...)手动将字段标记为无效(导致验证失败)Validators are not run on undefined values. The only exception is the验证程序不是在未定义的值上运行的。唯一的例外是required
validator.required
验证器。When you call Model#save, Mongoose also runs subdocument validation.当您调用Model#save
时,Mongoose还会运行子文档验证。If an error occurs, your Model#save promise rejects如果发生错误,您的Model#save承诺将被拒绝Validation is customizable验证是可自定义的
const schema = new Schema({
name: {
type: String,
required: true
}
});
const Cat = db.model('Cat', schema);
// This cat has no name :(
const cat = new Cat();
let error;
try {
await cat.save();
} catch (err) {
error = err;
}
assert.equal(error.errors['name'].message,
'Path `name` is required.');
error = cat.validateSync();
assert.equal(error.errors['name'].message,
'Path `name` is required.');
Built-in Validators内置验证程序Custom Error Messages自定义错误消息Theunique
Option is Not a Validatorunique
选项不是验证程序Custom Validators自定义验证程序Async Custom Validators异步自定义验证程序Validation Errors验证错误Cast Errors强制转换错误Global SchemaType Validation全局架构类型验证Required Validators On Nested Objects嵌套对象上所需的验证程序Update Validators更新验证程序Update Validators and更新验证程序和this
this
Update Validators Only Run On Updated Paths更新验证程序仅在更新的路径上运行Update Validators Only Run For Some Operations仅对某些操作运行更新验证程序
Built-in Validators内置验证程序
Mongoose has several built-in validators.Mongoose有几个内置的验证器。
All SchemaTypes have the built-in required validator.所有SchemaTypes都有内置的必需验证器。The required validator uses the SchemaType's所需的验证器使用SchemaType的checkRequired()
function to determine if the value satisfies the required validator.checkRequired()
函数来确定该值是否满足所需的验证程序。- Numbers
have具有min
andmax
validators.min
和max
验证器。 - Strings
have具有enum
,match
,minLength
, andmaxLength
validators.enum
、match
、minLength
和maxLength
验证器。
Each of the validator links above provide more information about how to enable them and customize their error messages.上面的每个验证器链接都提供了关于如何启用它们和自定义它们的错误消息的更多信息。
const 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;
}
}
});
const Breakfast = db.model('Breakfast', breakfastSchema);
const badBreakfast = new Breakfast({
eggs: 2,
bacon: 0,
drink: 'Milk'
});
let 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?');
Custom Error Messages自定义错误消息
You can configure the error message for individual validators in your schema. 您可以为模式中的各个验证器配置错误消息。There are two equivalent ways to set the validator error message:有两种等效的方法可以设置验证器错误消息:
Array syntax:数组语法:min: [6, 'Must be at least 6, got {VALUE}']
Object syntax:对象语法:enum: { values: ['Coffee', 'Tea'], message: '{VALUE} is not supported' }
Mongoose also supports rudimentary templating for error messages. Mongoose还支持错误消息的基本模板。Mongoose replaces Mongoose将{VALUE}
with the value being validated.{VALUE}
替换为正在验证的值。
const breakfastSchema = new Schema({
eggs: {
type: Number,
min: [6, 'Must be at least 6, got {VALUE}'],
max: 12
},
drink: {
type: String,
enum: {
values: ['Coffee', 'Tea'],
message: '{VALUE} is not supported'
}
}
});
const Breakfast = db.model('Breakfast', breakfastSchema);
const badBreakfast = new Breakfast({
eggs: 2,
drink: 'Milk'
});
const error = badBreakfast.validateSync();
assert.equal(error.errors['eggs'].message,
'Must be at least 6, got 2');
assert.equal(error.errors['drink'].message, 'Milk is not supported');
The unique
Option is Not a Validatorunique
选项不是验证程序
unique
Option is Not a ValidatorA common gotcha for beginners is that the 对于初学者来说,一个常见的难题是模式的unique
option for schemas is not a validator. unique
选项不是验证器。It's a convenient helper for building MongoDB unique indexes. 它是构建MongoDB唯一索引的方便助手。See the FAQ for more information.有关更多信息,请参阅常见问题解答。
const uniqueUsernameSchema = new Schema({
username: {
type: String,
unique: true
}
});
const U1 = db.model('U1', uniqueUsernameSchema);
const U2 = db.model('U2', uniqueUsernameSchema);
const dup = [{ username: 'Val' }, { username: 'Val' }];
// Race condition! This may save successfully, depending on whether MongoDB built the index before writing the 2 docs.比赛条件!这可能会成功保存,这取决于MongoDB是否在编写2个文档之前构建了索引。
U1.create(dup).
then(() => {
}).
catch(err => {
});
// You need to wait for Mongoose to finish building the `unique` index before writing. 在编写之前,您需要等待Mongoose完成唯一索引的构建。
// You only need to build indexes once for a given collection, so you normally don't need to do this in production.对于给定的集合,只需要构建一次索引,所以在生产中通常不需要这样做。
// But, if you drop the database between tests, you will need to use `init()` to wait for the index build to finish.但是,如果在测试之间丢弃数据库,则需要使用`init()`来等待索引构建完成。
U2.init().
then(() => U2.create(dup)).
catch(error => {
// `U2.create()` will error, but will *not* be a mongoose validation error, it will be a duplicate key error.将出错,但**不会**是mongoose验证错误,这将是一个重复的密钥错误。
// See: 请参阅https://masteringjs.io/tutorials/mongoose/e11000-duplicate-key
assert.ok(error);
assert.ok(!error.errors);
assert.ok(error.message.indexOf('duplicate key error') !== -1);
});
Custom Validators自定义验证程序
If the built-in validators aren't enough, you can define custom validators to suit your needs.如果内置的验证器还不够,您可以定义自定义验证器来满足您的需求。
Custom validation is declared by passing a validation function. 通过传递验证函数来声明自定义验证。You can find detailed instructions on how to do this in the 您可以在SchemaType#validate()
API docs.SchemaType#validate()
API文档中找到有关如何执行此操作的详细说明。
const userSchema = new Schema({
phone: {
type: String,
validate: {
validator: function(v) {
return /\d{3}-\d{3}-\d{4}/.test(v);
},
message: props => `${props.value} is not a valid phone number!`
},
required: [true, 'User phone number required']
}
});
const User = db.model('user', userSchema);
const user = new User();
let 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`验证成功!电话号码已定义并符合`DDD-DDD-DDD`
error = user.validateSync();
assert.equal(error, null);
Async Custom Validators异步自定义验证程序
Custom validators can also be asynchronous. If your validator function returns a promise (like an 自定义验证器也可以是异步的。如果您的验证器函数返回一个promise(像async
function), mongoose will wait for that promise to settle. async
函数一样),mongoose将等待该promise结束。If the returned promise rejects, or fulfills with the value 如果返回的promise拒绝,或者用false
, Mongoose will consider that a validation error.false
值实现,Mongoose将认为这是一个验证错误。
const userSchema = new Schema({
name: {
type: String,
// You can also make a validator async by returning a promise.您还可以通过返回promise来使验证器异步。
validate: () => Promise.reject(new Error('Oops!'))
},
email: {
type: String,
// There are two ways for an promise-based async validator to fail:基于promise的异步验证器有两种失败方式:
// 1) If the promise rejects, Mongoose assumes the validator failed with the given error.如果promise拒绝,Mongoose将假定验证器失败并出现给定的错误。
// 2) If the promise resolves to `false`, Mongoose assumes the validator failed and creates an error with the given `message`.如果promise解析为false,Mongoose将假定验证器失败,并使用给定的`message`创建一个错误。
validate: {
validator: () => Promise.resolve(false),
message: 'Email validation failed'
}
}
});
const User = db.model('User', userSchema);
const user = new User();
user.email = 'test@test.co';
user.name = 'test';
let error;
try {
await user.validate();
} catch (err) {
error = err;
}
assert.ok(error);
assert.equal(error.errors['name'].message, 'Oops!');
assert.equal(error.errors['email'].message, 'Email validation failed');
Validation Errors验证错误
Errors returned after failed validation contain an 验证失败后返回的errors
object whose values are ValidatorError
objects. errors
包含一个错误对象,其值为ValidatorError
对象。Each ValidatorError has 每个ValidatorError都有kind
, path
, value
, and message
properties. kind
、path
、value
和message
属性。A ValidatorError also may have a ValidatorError也可能有一个reason
property. reason
属性。If an error was thrown in the validator, this property will contain the error that was thrown.如果在验证器中引发错误,则此属性将包含引发的错误。
const toySchema = new Schema({
color: String,
name: String
});
const 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');
const Toy = db.model('Toy', toySchema);
const toy = new Toy({ color: 'Green', name: 'Power Ranger' });
let error;
try {
await toy.save();
} catch (err) {
error = err;
}
// `error` is a ValidationError object是ValidationError对象
// `error.errors.color` is a ValidatorError object是ValidatorError对象
assert.equal(error.errors.color.message, 'Color `Green` not valid');
assert.equal(error.errors.color.kind, 'Invalid color');
assert.equal(error.errors.color.path, 'color');
assert.equal(error.errors.color.value, 'Green');
// If your validator throws an exception, mongoose will use the error message.如果您的验证器抛出异常,mongoose将使用错误消息。
// If your validator returns `false`, mongoose will use the 'Name `Power Ranger` is not valid' message.如果您的验证器返回`false`,mongoose将使用`Name Power Ranger is not valid`消息。
assert.equal(error.errors.name.message,
'Need to get a Turbo Man for Christmas');
assert.equal(error.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.如果您的验证器抛出了一个错误,那么`reason`属性将包含抛出的原始错误,包括原始堆栈跟踪。
assert.equal(error.errors.name.reason.message,
'Need to get a Turbo Man for Christmas');
assert.equal(error.name, 'ValidationError');
Cast Errors
Before running validators, Mongoose attempts to coerce values to the correct type. 在运行验证器之前,Mongoose尝试将值强制转换为正确的类型。This process is called casting the document. 这个过程称为铸造文档。If casting fails for a given path, the 如果给定路径的强制转换失败,error.errors
object will contain a CastError
object.error.errors
对象将包含一个CastError对象。
Casting runs before validation, and validation does not run if casting fails. 铸造在验证之前运行,如果铸造失败,则不运行验证。That means your custom validators may assume 这意味着您的自定义验证器可能会假设v
is null
, undefined
, or an instance of the type specified in your schema.v
为null
、undefined
或是您的模式中指定的类型的实例。
const vehicleSchema = new mongoose.Schema({
numWheels: { type: Number, max: 18 }
});
const Vehicle = db.model('Vehicle', vehicleSchema);
const doc = new Vehicle({ numWheels: 'not a number' });
const err = doc.validateSync();
err.errors['numWheels'].name; // 'CastError'
// 'Cast to Number failed for value "not a number" at path "numWheels"'
err.errors['numWheels'].message;
Global SchemaType Validation全局架构类型验证
In addition to defining custom validators on individual schema paths, you can also configure a custom validator to run on every instance of a given 除了在各个模式路径上定义自定义验证器之外,您还可以配置一个自定义验证器,使其在给定SchemaType
. SchemaType
的每个实例上运行。For example, the following code demonstrates how to make empty string 例如,以下代码演示如何使空字符串''
an invalid value for all string paths.''
成为所有字符串路径的无效值。
// Add a custom validator to all strings向所有字符串添加自定义验证器
mongoose.Schema.Types.String.set('validate', v => v == null || v > 0);
const userSchema = new Schema({
name: String,
email: String
});
const User = db.model('User', userSchema);
const user = new User({ name: '', email: '' });
const err = await user.validate().then(() => null, err => err);
err.errors['name']; // ValidatorError
err.errors['email']; // ValidatorError
Required Validators On Nested Objects嵌套对象上所需的验证程序
Defining validators on nested objects in mongoose is tricky, because nested objects are not fully fledged paths.在猫鼬中为嵌套对象定义验证器是很棘手的,因为嵌套对象不是完全成熟的路径。
let personSchema = new Schema({
name: {
first: String,
last: String
}
});
assert.throws(function() {
// This throws an error, because 'name' isn't a full fledged path这引发了一个错误,因为“name”不是一个完整的路径
personSchema.path('name').required(true);
}, /Cannot.*'required'/);
// To make a nested object required, use a single nested schema若要使嵌套对象成为必需,请使用单个嵌套模式
const nameSchema = new Schema({
first: String,
last: String
});
personSchema = new Schema({
name: {
type: nameSchema,
required: true
}
});
const Person = db.model('Person', personSchema);
const person = new Person();
const error = person.validateSync();
assert.ok(error.errors['name']);
Update Validators更新验证程序
In the above examples, you learned about document validation. 在上面的示例中,您了解了文档验证。Mongoose also supports validation for update(), updateOne(), updateMany(), and findOneAndUpdate() operations. Mongoose还支持update()
、updateOne()
、updateMany()
和findOneAndUpdate()
操作的验证。Update validators are off by default - you need to specify the 默认情况下,更新验证器是关闭的-您需要指定runValidators
option.runValidators
选项。
To turn on update validators, set the 要打开更新验证器,请为runValidators
option for update()
, updateOne()
, updateMany()
, or findOneAndUpdate()
. update()
、updateOne()
、updateMany()
或findOneAndUpdate()
设置runValidators
选项。Be careful: update validators are off by default because they have several caveats.请注意:更新验证器在默认情况下是关闭的,因为它们有几个注意事项。
const toySchema = new Schema({
color: String,
name: String
});
const Toy = db.model('Toys', toySchema);
Toy.schema.path('color').validate(function(value) {
return /red|green|blue/i.test(value);
}, 'Invalid color');
const opts = { runValidators: true };
let error;
try {
await Toy.updateOne({}, { color: 'not a color' }, opts);
} catch (err) {
error = err;
}
assert.equal(error.errors.color.message, 'Invalid color');
Update Validators and 更新验证程序和this
There are a couple of key differences between update validators and document validators. 更新验证器和文档验证器之间有几个关键区别。In the color validation function below, 在下面的颜色验证功能中,this
refers to the document being validated when using document validation. this
引用使用文档验证时要验证的文档。However, when running update validators, 但是,当运行更新验证器时,this
refers to the query object instead of the document. this
引用的是查询对象,而不是文档。Because queries have a neat 因为查询有一个整洁的.get()
function, you can get the updated value of the property you want..get()
函数,所以可以获得所需属性的更新值。
const 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`.当在`validate()`或`validateSync()`中运行时,验证器可以使用它访问文档。
// When running with update validators, `this` is the Query, **not** the document being updated!当使用更新验证器运行时`this`是查询,**而不是**正在更新的文档!
// Queries have a `get()` method that lets you get the updated value.查询有一个`get()`方法,用于获取更新后的值。
if (this.get('name') && this.get('name').toLowerCase().indexOf('red') !== -1) {
return value === 'red';
}
return true;
});
const Toy = db.model('ActionFigure', toySchema);
const toy = new Toy({ color: 'green', name: 'Red Power Ranger' });
// Validation failed: color: Validator failed for path `color` with value `green`验证失败:`color`值为`green`的路径颜色的验证程序失败
let error = toy.validateSync();
assert.ok(error.errors['color']);
const update = { color: 'green', name: 'Red Power Ranger' };
const opts = { runValidators: true };
error = null;
try {
await Toy.updateOne({}, update, opts);
} catch (err) {
error = err;
}
// Validation failed: color: Validator failed for path `color` with value `green`验证失败:`color`值为`green`的路径颜色的验证程序失败
assert.ok(error);
Update Validators Only Run On Updated Paths更新验证程序仅在更新的路径上运行
The other key difference is that update validators only run on the paths specified in the update. 另一个关键区别是更新验证器只在更新中指定的路径上运行。For instance, in the below example, because 'name' is not specified in the update operation, update validation will succeed.例如,在以下示例中,由于更新操作中未指定“name”,因此更新验证将成功。
When using update validators, 当使用更新验证器时,只有当您尝试显式required
validators only fail when you try to explicitly $unset
the key.$unset
密钥时,required
验证器才会失败。
const kittenSchema = new Schema({
name: { type: String, required: true },
age: Number
});
const Kitten = db.model('Kitten', kittenSchema);
const update = { color: 'blue' };
const opts = { runValidators: true };
// Operation succeeds despite the fact that 'name' is not specified尽管未指定'name',但操作仍成功
await Kitten.updateOne({}, update, opts);
const unset = { $unset: { name: 1 } };
// Operation fails because 'name' is required操作失败,因为需要“name”
const err = await Kitten.updateOne({}, unset, opts).then(() => null, err => err);
assert.ok(err);
assert.ok(err.errors['name']);
Update Validators Only Run For Some Operations仅对某些操作运行更新验证程序
One final detail worth noting: update validators only run on the following update operators:最后一个值得注意的细节是:更新验证器仅在以下更新运算符上运行:
$set
$unset
$push
$addToSet
$pull
$pullAll
For instance, the below update will succeed, regardless of the value of 例如,无论number
, because update validators ignore $inc
.number
的值是多少,下面的更新都会成功,因为更新验证器忽略了$inc
。
Also, 此外,$push
, $addToSet
, $pull
, and $pullAll
validation does not run any validation on the array itself, only individual elements of the array.$push
、$addToSet
、$pull
和$pullAll
验证不会对数组本身运行任何验证,只对数组的单个元素运行验证。
const testSchema = new Schema({
number: { type: Number, max: 0 },
arr: [{ message: { type: String, maxlength: 10 } }]
});
// Update validators won't check this, so you can still `$push` 2 elements更新验证器不会检查这一点,所以您仍然可以`$push` 2个元素
// onto the array, so long as they don't have a `message` that's too long.到数组中,只要他们没有太长的`message`。
testSchema.path('arr').validate(function(v) {
return v.length < 2;
});
const Test = db.model('Test', testSchema);
let update = { $inc: { number: 1 } };
const opts = { runValidators: true };
// There will never be a validation error here这里永远不会有验证错误
await Test.updateOne({}, update, opts);
// This will never error either even though the array will have at least 2 elements.即使数组将至少有2个元素,这也不会出错。
update = { $push: [{ message: 'hello' }, { message: 'world' }] };
await Test.updateOne({}, update, opts);
Next Up下一步
Now that we've covered 现在我们已经介绍了Validation
, let's take a look at Middleware.Validation
,让我们来看看中间件。