为什么猫鼬不验证更新?

我有这个密码

var ClientSchema = new Schema({
name: {type: String, required: true, trim: true}
});


var Client = mongoose.model('Client', ClientSchema);

使用 Express,我用这段代码创建一个新客户端

var client = new Client(req.body);
client.save(function(err, data) {
....
});

如果表单中的 name 字段为空,猫鼬就不允许创建客户机,因为我将它设置为模式中所需的。另外,如果我在名字前后留下空格,猫鼬会在保存之前删除这些空格。

现在,我尝试用这段代码更新一个客户端

var id = req.params.id;
var client = req.body;
Client.update({_id: id}, client, function(err) {
....
});

它允许我更改名称,但是如果我在表单上保留它为空,猫鼬不会验证并保存一个空名称。如果在名称之前和之后添加空格,它将使用空格保存名称。

为什么猫鼬在保存时验证,而不是在更新时验证? 我做的方式不对吗?

Mongodb: 2.4.0 猫鼬: 3.6 Express: 3.1.0 节点: 0.10.1

34553 次浏览

您没有做错任何事情,validation在 Mongoose 中作为内部中间件实现,中间件在 update期间不会执行,因为它基本上是传递给本机驱动程序的。

如果希望验证客户端更新,则需要对要更新的对象 find应用新的属性值(参见下划线的 extend方法) ,然后对其调用 save

猫鼬4.0更新

正如在评论和 victorkohl 的回答中指出的那样,当您在 update调用中包含 runValidators: true选项时,Mongoose 现在支持对 $set$unset操作符字段的验证。

在 Mongoose 4.0中,可以运行验证程序update()findOneAndUpdate()上使用新的标志 runValidators: true

Mongoose 4.0引入了在 update()和 打开此选项将运行验证程序 对于 update()调用尝试调用 $set$unset的所有字段。

例如,给定 OP 的模式:

const ClientSchema = new Schema({
name: {type: String, required: true, trim: true}
});


const Client = mongoose.model('Client', ClientSchema);

在每次更新时传递标志

你可以这样使用新的标志:

const id = req.params.id;
const client = req.body;
Client.update({_id: id}, client, { runValidators: true }, function(err) {
....
});

pre钩子上使用标志

如果你不想在每次更新的时候都设置标志,你可以为 findOneAndUpdate()设置一个 pre挂钩:

// Pre hook for `findOneAndUpdate`
ClientSchema.pre('findOneAndUpdate', function(next) {
this.options.runValidators = true;
next();
});

然后,您可以使用 update()验证器,而不必每次都传递 runValidators 标志。

在您的模型中,例如,Category ory.js 文件:

const CategorySchema = mongoose.Schema({
category_name : {
type : String,
required : [true, 'Category Name Is Required !'],
trim : true,
maxlength : [30, 'Category Name Is To Long !'],
unique : true,
});
const Category = module.exports = mongoose.model("Category",CategorySchema);

在你的路线文件里:

router.put("/",(req,res,next)=>{
Category.findOneAndUpdate(
{_id : req.body.categoryId},
{$set : {category_name : req.body.category_name} },
**{runValidators: true}**, function(err,result) {
if(err){
if(err.code === 11000){
var duplicateValue = err.message.match(/".*"/);
res.status(200).json({"defaultError":duplicateValue[0]+" Is Already Exsist !"});
}else{
res.status(200).json({"error":err.message} || {"defaultError":'Error But Not Understood !'});
}
}else{
console.log("From category.js (Route File) = "+result);
res.status(200).json({"success":"Category Updated Successfully!!"});
}
});

MongoDB 默认情况下不对更新运行验证。

为了在更新发生时默认进行验证,在连接到 MongoDB 之前,您可以只设置像下面这样的全局设置:

mongoose.set('runValidators', true); // here is your global setting


mongoose.connect(config.database, { useNewUrlParser: true });
mongoose.connection.once('open', () => {
console.log('Connection has been made, start making fireworks...');
}).on('error', function (error) {
console.log('Connection error:', error);
});

因此,任何内置或自定义验证也将在任何更新上运行

如果你在猫鼬的配置中加入这个选项,它就可以工作了:

mongoose.set('runValidators', true)

通过设置选项 runValidators: true,可以在更新时运行验证。

例子一:


const Kitten = db.model('Kitten', kittenSchema);


const update = { color: 'blue' };
const opts = { runValidators: true };
Kitten.updateOne({}, update, opts, function() {
// code
});


例二:

const Kitten = db.model('Kitten', kittenSchema);


const update = { color: 'blue' };
const opts = { runValidators: true };
Kitten.updateOne(
{
_id: req.params.id
},
{
$set: { ...update },
},
opts
).then(result => {
// code
})

延伸阅读: https://mongoosejs.com/docs/validation.html#update-validators

如果在 findOneAndUpdate选项中使用 upsert,则接受的答案不起作用。解决这个问题的方法是创建一个模型静态方法,该方法先执行 findOne,然后在引擎盖下执行 updateOnecreatecreate会自动运行验证。

export async function findOneAndUpdateWithValidation(
this: LocationModel,
filter: FilterQuery<LocationDocument>,
update: UpdateQuery<LocationDocument>,
options?: QueryOptions
) {
const documentFound = await this.findOne(filter);
  

if (!documentFound) return this.create(update);


return this.updateOne(filter, update, options);
}


locationSchema.statics = {
findOneAndUpdateWithValidation
}