mongoosevalidation器

我有2个模式,其中包含validation器,PhotosSchema,UserSchema。 因为mongoose'更新'function使用本地mongo驱动程序(据我所知),它不会触发和validation检查。 因此在两个模式中我都这样做:

PhotosSchema.pre('update', function(next) { this.options.runValidators = true; next(); }) 

在照片模式中,它效果很好! 它在每次更新之前都会运行validation。 但在用户,由于某种原因,它不会触发validation,我清楚地看到所有的validation,当我检查'这'在'更新'前,但我真的不明白什么是它不会甚至不会调用该函数。

文件附在这里:)照片模式:

 '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); 

根据Mongoose Validation文档 ,您可以在更新数据时运行validation器,但是您必须明确告诉Mongoose运行它们。

这是来自上面链接的“ 更新validation器”部分的引用:

Mongoose还支持update()和findOneAndUpdate()操作的validation。 在Mongoose 4.x中,更新validation器默认是closures的 – 您需要指定runValidators选项。

要打开更新validation器,请为update()或findOneAndUpdate()设置runValidators选项。 小心:更新validation器默认closures,因为他们有几个警告。