Mongoose findOneAndUpdate启用runValidators时验证错误问题解决
问题重现
在Express.js项目中使用Project.findOneAndUpdate实现项目更新接口,已开启runValidators: true来调用projectModel.ts中的验证规则。新增项目时,advance字段的验证逻辑(需≥0且≤budget)正常生效,但更新操作中,即使传入advance=900、budget=10000(完全符合验证条件),仍会触发错误提示:'Advance must be a positive number and less than or equal to the budget',且要求不能使用save()方法解决。
问题原因
Mongoose在findOneAndUpdate开启runValidators: true时,默认使用query上下文:验证器中的this指向的是传入的更新数据对象,而非原文档与更新数据合并后的完整文档。如果更新时仅传入advance未传入budget,this.budget会是undefined,导致验证逻辑判定失败;即使同时传入budget,若未指定正确的上下文,验证器也无法正确关联字段间的约束关系。
解决方案
在findOneAndUpdate的选项中添加context: 'document',让验证器的this指向原文档与更新数据合并后的完整文档,确保验证逻辑能正确获取budget的值(无论是原文档的还是更新后的)。
代码示例
projectModel.ts(验证规则)
import mongoose from 'mongoose'; const projectSchema = new mongoose.Schema({ budget: { type: Number, required: true, min: 0 }, advance: { type: Number, validate: { validator: function(v: number) { // this指向合并后的完整文档,可正确获取budget值 return v >= 0 && v <= this.budget; }, message: 'Advance must be a positive number and less than or equal to the budget' } } }); export const Project = mongoose.model('Project', projectSchema);
projectController.ts(更新接口)
import { Request, Response } from 'express'; import { Project } from './projectModel'; export const updateProjectDetails = async (req: Request, res: Response) => { try { const { id } = req.params; const updateData = req.body; const updatedProject = await Project.findOneAndUpdate( { _id: id }, updateData, { new: true, // 返回更新后的完整文档 runValidators: true, // 启用模型验证规则 context: 'document' // 切换验证器上下文为完整文档 } ); if (!updatedProject) { return res.status(404).json({ message: '项目不存在' }); } res.status(200).json(updatedProject); } catch (error) { res.status(400).json({ message: (error as Error).message }); } };
测试请求参数
同时更新advance和budget
{ "advance": 900, "budget": 10000 }
仅更新advance(使用原文档的budget验证)
{ "advance": 900 }
说明
设置context: 'document'后,Mongoose会自动合并原文档数据与传入的更新数据,验证器可以正常访问到budget字段的值,从而正确执行advance的范围验证逻辑,解决更新时的误触发错误问题。
内容的提问来源于stack exchange,提问作者Samiul Karim

