猫鼬验证员

时间:2016-05-25 18:52:54

标签: node.js mongodb validation mongoose schema

我有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);

1 个答案:

答案 0 :(得分:1)

根据Mongoose Validation docs,您可以在更新数据时运行验证程序,但必须明确告诉Mongoose运行它们。

这是来自上面链接的 更新验证器 部分的引用:

  

Mongoose还支持update()和findOneAndUpdate()的验证   操作。在Mongoose 4.x中,默认情况下更新验证器已关闭 -   你需要指定runValidators选项。

     

要打开更新验证程序,请为其设置runValidators选项   update()或findOneAndUpdate()。注意:更新验证器已关闭   默认情况下,因为他们有几个警告。