使用 Mongoose Schema 防止字段修改

Posted

技术标签:

【中文标题】使用 Mongoose Schema 防止字段修改【英文标题】:Prevent field modification with Mongoose Schema 【发布时间】:2018-11-05 17:43:38 【问题描述】:

当你定义一个新的 Mongoose Schema 时,有什么方法可以设置一个“不可修改”设置的字段(例如类型、必需等)?这意味着一旦创建了新文档,就无法更改此字段。

例如,像这样的:

var userSchema = new mongoose.Schema(
  username: 
    type: String,
    required: true,
    unmodifiable: true
  
)

【问题讨论】:

【参考方案1】:

从 Mongoose 5.6.0 版本开始,我们可以在模式中使用immutable: true(与前面提到的mongoose-immutable 包上的答案完全相同)。典型的用例是时间戳,但在你的情况下,username 是这样的:

const userSchema = new mongoose.Schema(
  username: 
    type: String,
    required: true,
    immutable: true
  
);

如果您尝试更新该字段,Mongoose 将忽略修改。


比 OP 的要求更进一步,现在有了 Mongoose 5.7.0,我们可以有条件地设置 immutable 属性。

const userSchema = new mongoose.Schema(
  username: 
    type: String,
    required: true,
    immutable: doc => doc.role !== 'ADMIN'
  ,
  role: 
    type: String,
    default: 'USER',
    enum: ['USER', 'MODERATOR', 'ADMIN'],
    immutable: true
  
);

来源:What's New in Mongoose 5.6.0: Immutable Properties 和 What's New in Mongoose 5.7: Conditional Immutability, Faster Document Arrays。

【讨论】:

【参考方案2】:

您只能在 userSchema.pre save 中使用 Mongoose:

if (this.isModified('modified query')) 
    return next(new Error('Trying to modify restricted data'));

return next();

【讨论】:

【参考方案3】:

我在修改字段时遇到了同样的问题。

试试https://www.npmjs.com/package/mongoose-immutable-plugin

插件将拒绝对字段的每次修改尝试,它适用于

    更新 UpdateOne FindOneAndUpdate UpdateMany 重新保存

它支持数组、嵌套对象等类型的字段并保护深度不变性。

插件还将更新选项处理为 $set、$inc 等。

【讨论】:

【参考方案4】:

请注意,文档明确指出,在使用带有更新标识符/名称的函数时,不会触发“pre”中间件:

虽然在使用更新时会将值转换为相应的类型,但以下内容不适用: - 默认值 - 二传手 - 验证器 - 中间件

如果您需要这些功能,请使用首先检索文档的传统方法。

Model.findOne( name: 'borne' , function (err, doc) if (err) .. doc.name = 'jason bourne'; doc.save(callback); )

因此要么通过 mongooseAPI 采用上述方式,它可以触发中间件(如 desoares 答案中的“pre”)或触发您自己的验证器,例如:

const theOneAndOnlyName = 'Master Splinter';
const UserSchema = new mongoose.Schema(
  username: 
    type: String,
    required: true,
    default: theOneAndOnlyName
    validate: 
      validator: value => 
        if(value != theOneAndOnlyName) 
          return Promise.reject('PATH do not specify this field, it will be set automatically');
          // message can be checked at error.errors['username'].reason
        
        return true;
      ,
      message: 'PATH do not specify this field, it will be set automatically'
    
  
);

或始终使用 runValidators: true 形式的附加“选项”参数调用任何更新函数(例如“findByIdAndUpdate”和朋友),例如:

const splinter = new User( username: undefined );
User.findByIdAndUpdate(splinter._id,  username: 'Shredder' ,  runValidators: true )
  .then(() => User.findById(splinter._id))
  .then(user => 
    assert(user.username === 'Shredder');

    done();
  )
  .catch(error => console.log(error.errors['username'].reason));

您还可以以非标准方式使用验证器功能,即:

...
validator: function(value) 
  if(value != theOneAndOnlyName) 
    this.username = theOneAndOnlyName;
  
  return true;

...

这不会抛出“ValidationError”,而是悄悄地覆盖指定的值。它仍然只在使用 save() 或使用指定的验证选项参数更新函数时这样做。

【讨论】:

【参考方案5】:

您可以使用Mongoose Immutable。这是一个小包,您可以使用以下命令安装,它允许您使用“不可变”属性。

npm install mongoose-immutable --save

然后使用它:

var userSchema = new mongoose.Schema(
    username: 
        type: String,
        required: true,
        immutable: true
    
);
userSchema.plugin(immutablePlugin);

【讨论】:

以上是关于使用 Mongoose Schema 防止字段修改的主要内容,如果未能解决你的问题,请参考以下文章

使用 TypeScript 在 mongoose.Schema 的 `required` 字段中使用函数

访问 mongoose.Schema.methods 中的“select: false”字段

mongoose Schema 中的字段可以都有哪些选项?

使用 mongoose 在 mongodb 中保存多个字段数组

Typescript Mongoose 在 VS Code 中获取 Schema 字段的 IntelliSense 或警告

无法将带有点的键(键路径)添加到具有以下类型的 Mongoose 字段:Schema.Types.Mixed