Node.js 猫鼬验证器

Node.js 猫鼬验证器,node.js,mongodb,validation,mongoose,schema,Node.js,Mongodb,Validation,Mongoose,Schema,我有两个模式,其中包含验证器、PhotoSchema和UserSchema。 因为据我所知,mongoose'update'函数使用本机mongo驱动程序,所以它不会触发和验证检查。 因此,在这两种模式中,我都这样做: PhotosSchema.pre('update', function(next) { this.options.runValidators = true; next(); }) 在照片模式中,它工作得很好!它在每次更新之前都会运行validate。 但是在用户中,由于

我有两个模式,其中包含验证器、PhotoSchema和UserSchema。 因为据我所知,mongoose'update'函数使用本机mongo驱动程序,所以它不会触发和验证检查。 因此,在这两种模式中,我都这样做:

PhotosSchema.pre('update', function(next) {
  this.options.runValidators = true;
  next();
})
在照片模式中,它工作得很好!它在每次更新之前都会运行validate。 但是在用户中,由于某些原因,它不会触发验证器,我在“更新”之前检查“this”时可以清楚地看到所有验证器,但老实说,我无法找出它甚至不会调用函数的原因

附件如下: 照片架构:

'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运行验证器

这是来自更新验证程序部分的引用,摘自上面的链接:

Mongoose还支持对update和findOneAndUpdate进行验证 操作。在Mongoose4.x中,更新验证器在默认情况下是关闭的- 您需要指定runValidators选项

要启用更新验证器,请为设置runValidators选项 更新或findOneAndUpdate。小心:更新验证程序已关闭 默认情况下,因为它们有几个警告


在使用验证器进行更新时有哪些注意事项?