如何在 Sequelize 中的 setterMethod 之前运行模型验证?

Posted

技术标签:

【中文标题】如何在 Sequelize 中的 setterMethod 之前运行模型验证?【英文标题】:How can I run model validations before the setterMethod in Sequelize? 【发布时间】:2015-11-16 22:14:24 【问题描述】:

我想保存一个散列密码。我为此使用了 setterMethod:

module.exports = (sequelize, DataTypes) ->
  sequelize.define 'User',
    # other model fields [...]
    password:
      type: DataTypes.STRING
      validate: notEmpty: msg: 'You need to set a password.'
      set: (pw) ->
        salt = bcrypt.genSaltSync(10)
        hash = bcrypt.hashSync(pw, salt)
        @setDataValue('password', hash)

setter 首先运行。一个空字符串密码 ('') 被散列成一个非空密码 (比如$2a$10$pDDIGnV.r47i9YOv0Fls/euQ0yYvfyq8T1SyP9VRQsTUAqptNmxXO)。

验证器验证时,密码不再为空。

如何在 setter 之前验证密码?

我查看了hooks,但他们也没有提到二传手。

我正在使用sequelize@2.1.3

【问题讨论】:

【参考方案1】:

我通过使用两个字段解决了这个问题,一个是处理输入和验证的VIRTUAL,另一个是保存哈希密码的STRING

这个例子不是coffeescript,但你应该可以轻松翻译。

password_hash: 
  type: DatabaseTypes.STRING,
  allowNull: false,
  validate: 
    notEmpty: true,
  ,
,
password: 
  type: DatabaseTypes.VIRTUAL,
  allowNull: false,
  // note that arrow functions cannot access "this", so use the form:
  set: function setPassword(val) 
    // trigger validation on "password" field
    this.setDataValue('password', val);

    // hash the password, this can be done in one step by passing the
    // number of salt rounds instead of the salt string.
    this.setDataValue('password_hash', bcrypt.hashSync(val, 10));
  ,
  validate: 
    notEmpty: 
       message: 'You need to set a password.',
    ,
  ,
,

当您对用户进行身份验证时,将输入的密码与User.password_hash 进行比较,而不是User.password

instanceMethods: 
  // authenticate user given a password
  authenticate(password) 
    return bcrypt.compareSync(password, this.password_hash);
  ,
,

然后您可以调用此实例方法来验证User

User.findById(userId)
.then((user) => 
  if (user.authenticate(password)) 
    console.log('Authenticated');
   else 
    console.log('Not authenticated');
  
);

【讨论】:

【参考方案2】:

您可以使用 here 所述的 beforeCreate 挂钩来完成此操作

基本上,您的模型代码看起来像这样

User.init(
  
    password: 
      type: DataTypes.TEXT,
      allowNull: false,
      validate: 
        len: 
          args: [6],
          msg: "Minimum password length is 6 characters",
        ,
        notEmpty: 
          args: [true],
          msg: "Please enter a password",
        ,
        notNull: 
          args: [true],
          msg: "Please enter a password",
        ,
      ,
    ,
  ,
  
    hooks: 
      beforeCreate: async (user) =>
        (user.password = await bcrypt.hash(user.password, 10)),
    ,
    sequelize,
    modelName: "User",
  ,
);

查看 Sequelize hooks API 参考 here

【讨论】:

【参考方案3】:

所以我使用了 beforeSave 钩子,用于插入和更新。我对其进行了测试似乎可以解决问题 - 验证也可以按预期工作。

User.init(
  
    // attributes
    email: 
      type: Sequelize.STRING,
      allowNull: false,
      unique: true,
      validate: 
        isEmail: 
          msg: "Email address is entered is invalid",
        ,
      ,
    ,
    password: 
      type: Sequelize.STRING,
      allowNull: false,
      validate: 
        notEmpty: 
          msg: "Password cannot be empty",
        ,
        notNull: 
          msg: "Password cannot be null",
        ,
      ,
    ,
  ,
  
    hooks: 
      beforeSave: (user, options) => 
        if (options.fields.includes("password")) 
          user.password = bcrypt.hashSync(user.password, 10)
        
      ,
    ,
    sequelize,
    modelName: "user",
    underscored: true,
  
)

【讨论】:

以上是关于如何在 Sequelize 中的 setterMethod 之前运行模型验证?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 sequelize 基于 sqlite3 中的“日期”列进行查询?

如何使用node.js控制sequelize中的内连接查询?

如何在 Sequelize 中的导入模型中创建自定义方法或函数

如何通过节点js中的sequelize更新关联记录?

复数如何在 Sequelize 中工作?

Nodejs / PostgreSQL sequelize - 如何更新/更改数据库中的模型?