我有2个包含验证器的模式,PhotosSchema,UserSchema。 因为猫鼬的更新'函数使用本机mongo驱动程序(据我所知),它不会触发和验证检查。 因此在两种模式中我都这样做:
PhotosSchema.pre('update', function(next) {
this.options.runValidators = true;
next();
})
在照片架构中,效果很棒!它确实在每次更新之前运行验证。 但是在用户中,由于某些原因它不会触发验证器,当我检查这个'这个时,我清楚地看到了所有的验证器。在预先更新',但老实说,我无法弄清楚它甚至不会调用该功能的原因。
附在这里的文件:) 照片架构:
'use strict';
var mongoose = require('mongoose'),
crate = require('mongoose-crate'),
S3 = require('mongoose-crate-s3');
var path = require('path');
var consts = require('../../config/environment/shared')
var PhotosSchema = new mongoose.Schema({
userId :{
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
photoIndex: {
type: Number,
validate: [photosLimit, '{PATH} exceeds the limit of ' + consts.MAX_PHOTOS]
}
}, {timestamps: true});
function photosLimit(val){
return val < consts.MAX_PHOTOS;
}
PhotosSchema.createFilePathAtAws = function(attachment) { // where the file is stored in the bucket - defaults to this function
return '/'+ attachment.userId+ '/' + attachment.photoIndex;
}
PhotosSchema.plugin(crate, {
storage: new S3({
key: process.env.AMAZON_KEY,
secret: process.env.AMAZON_SECRET,
bucket: process.env.AMAZON_BUCKET,
acl: 'public-read-write', // defaults to public-read
region: 'us-standard', // defaults to us-standard
path: PhotosSchema.createFilePathAtAws
}),
fields: {
file: {}
}
});
/**
* Update in mongoose bypass the validators and is using the mongo driver for updating.
* therefor for each update we will set the flag of run validators to true before updating.
*/
PhotosSchema.pre('update', function(next) {
this.options.runValidators = true;
next();
})
export default mongoose.model('Photos', PhotosSchema);
用户架构:
'use strict';
import crypto from 'crypto';
import mongoose from 'mongoose';
import Photos from '../photos/photos.model'
var PhotosSchema = Photos.schema;
mongoose.Promise = require('bluebird');
import {Schema} from 'mongoose';
var consts = require('../../config/environment/shared');
const AUTH_TYPES = consts.AUTH_TYPES;
var UserSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
lowercase: true,
required: true,
},
sex: {
type: String,
lowercase: true,
enum: ['male', 'female'],
required: true,
validate: [friendsLimit, '{PATH} exceeds the limit of ' + consts.MAX_FRIENDS]
},
preferredSex: {
type: String,
lowercase: true,
enum: ['male', 'female'],
required: true
},
role: {
type: String,
default: 'user',
required: true
},
about: String,
preferredAge: {
minAge: {
type: Number,
required: true
},
maxAge: {
type: Number,
required: true
},
validate: [ageRangeValidate, '{PATH} Age range must be: ' + consts.MIN_AGE + '-' + consts.MAX_AGE]
},
team: {
type: [{
type: Schema.Types.ObjectId,
ref: 'Users',
required: true
}],
validate: [friendsLimit, '{PATH} exceeds the limit of ' + consts.MAX_FRIENDS]
},
usedUsers: {
type: Object,
default: {}
},
matches: {
type: Object,
default: {}
},
photos: {
type: [PhotosSchema],
validate: [photosLimit, '{PATH} exceeds the limit of 5']
},
birthdate: {
type: 'Date',
required: true
},
profilepic: String,
password: {
type: String,
},
provider: {
type: String,
required: true
},
salt: String,
facebook: {},
google: {},
github: {}
}, {timestamps: true});
// validators
function ageRangeValidate(val) {
// TODO
return true;
}
function friendsLimit(val) {
console.log("USER MODEL VALIDATE ");
return val.length <= consts.MAX_FRIENDS;
}
function photosLimit(val) {
console.log("USER MODEL VALIDATE - m in user.model, at PhotosLimit function, this user has " + val.length + "Photos");
return val.length < consts.MAX_PHOTOS;
}
/**
* Virtuals
*/
// Public profile information
UserSchema
.virtual('profile')
.get(function () {
return {
'name': this.name,
'role': this.role
};
});
// Non-sensitive info we'll be putting in the token
UserSchema
.virtual('token')
.get(function () {
return {
'_id': this._id,
'role': this.role
};
});
/**
* Validations
*/
// Validate empty email
UserSchema
.path('email')
.validate(function (email) {
console.warn("USER MODEL VALIDATE ");
if (AUTH_TYPES.indexOf(this.provider) !== -1) {
return true;
}
return email.length;
}, 'Email cannot be blank');
// Validate empty password
UserSchema
.path('password')
.validate(function (password) {
console.warn("USER MODEL VALIDATE ");
if (AUTH_TYPES.indexOf(this.provider) !== -1) {
return true;
}
return password.length;
}, 'Password cannot be blank');
// Validate email is not taken
UserSchema
.path('email')
.validate(function (value, respond) {
console.warn("USER MODEL VALIDATE ");
var self = this;
return this.constructor.findOne({email: value}).exec()
.then(function (user) {
if (user) {
if (self.id === user.id) {
return respond(true);
}
return respond(false);
}
return respond(true);
})
.catch(function (err) {
throw err;
});
}, 'The specified email address is already in use.');
var validatePresenceOf = function (value) {
return value && value.length;
};
/**
* Pre-save hook
*/
UserSchema
.pre('save', function (next) {
// Handle new/update passwords
if (!this.isModified('password')) {
return next();
}
if (!validatePresenceOf(this.password) && AUTH_TYPES.indexOf(this.provider) === -1) {
return next(new Error('Invalid password'));
}
// Make salt with a callback
this.makeSalt((saltErr, salt) => {
if (saltErr) {
return next(saltErr);
}
this.salt = salt;
this.encryptPassword(this.password, (encryptErr, hashedPassword) => {
if (encryptErr) {
return next(encryptErr);
}
this.password = hashedPassword;
next();
});
});
});
/**
* Methods
*/
UserSchema.methods = {
/**
* Authenticate - check if the passwords are the same
*
* @param {String} password
* @param {Function} callback
* @return {Boolean}
* @api public
*/
authenticate(password, callback) {
if (!callback) {
return this.password === this.encryptPassword(password);
}
this.encryptPassword(password, (err, pwdGen) => {
if (err) {
return callback(err);
}
if (this.password === pwdGen) {
callback(null, true);
} else {
callback(null, false);
}
});
},
/**
* Make salt
*
* @param {Number} byteSize Optional salt byte size, default to 16
* @param {Function} callback
* @return {String}
* @api public
*/
makeSalt(byteSize, callback) {
var defaultByteSize = 16;
if (typeof arguments[0] === 'function') {
callback = arguments[0];
byteSize = defaultByteSize;
} else if (typeof arguments[1] === 'function') {
callback = arguments[1];
}
if (!byteSize) {
byteSize = defaultByteSize;
}
if (!callback) {
return crypto.randomBytes(byteSize).toString('base64');
}
return crypto.randomBytes(byteSize, (err, salt) => {
if (err) {
callback(err);
} else {
callback(null, salt.toString('base64'));
}
});
},
/**
* Encrypt password
*
* @param {String} password
* @param {Function} callback
* @return {String}
* @api public
*/
encryptPassword(password, callback) {
if (!password || !this.salt) {
return null;
}
var defaultIterations = 10000;
var defaultKeyLength = 64;
var salt = new Buffer(this.salt, 'base64');
if (!callback) {
return crypto.pbkdf2Sync(password, salt, defaultIterations, defaultKeyLength)
.toString('base64');
}
return crypto.pbkdf2(password, salt, defaultIterations, defaultKeyLength, (err, key) => {
if (err) {
callback(err);
} else {
callback(null, key.toString('base64'));
}
});
}
};
/**
* Update in mongoose bypass the validators and is using the mongo driver for updating.
* therefor for each update we will set the flag of run validators to true before updating.
*/
UserSchema.pre('update', function(next) {
this.options.runValidators = true;
next();
})
export default mongoose.model('User', UserSchema);
答案 0 :(得分:1)
根据Mongoose Validation docs,您可以在更新数据时运行验证程序,但必须明确告诉Mongoose运行它们。
这是来自上面链接的 更新验证器 部分的引用:
Mongoose还支持update()和findOneAndUpdate()的验证 操作。在Mongoose 4.x中,默认情况下更新验证器已关闭 - 你需要指定runValidators选项。
要打开更新验证程序,请为其设置runValidators选项 update()或findOneAndUpdate()。注意:更新验证器已关闭 默认情况下,因为他们有几个警告。