如何在Node.js与MongoDB中更新文档单个值并实现Joi局部校验
解决方案
你遇到的问题是Joi默认会校验schema中声明的所有required必填字段,而PATCH部分更新场景下只会传入需要修改的字段,只需要校验传入字段的格式合法性即可,不需要强制所有必填字段都传,按以下步骤修改即可:
步骤1:改造校验函数,支持新增/更新两种校验场景
给校验函数增加可选参数isUpdate,区分全量新增校验和部分更新校验:
function validateStudent(student, isUpdate = false){ // 先定义基础规则,不强制必填 const schema = Joi.object({ name:Joi.string().min(3).max(50), fatherName:Joi.string().min(3).max(50), email:Joi.string().min(5).max(50).email(), class:Joi.number().min(9).max(12), fee:Joi.number().min(4000).max(10000), address:Joi.string().min(1).max(255), phone:Joi.string().min(1).max(255), }) // 新增场景下所有字段强制必填 if (!isUpdate) { return schema.fork(Object.keys(schema.describe().keys), field => field.required()) .validate(student, { stripUnknown: true }) } // 更新场景下仅校验传入字段的格式,自动过滤未定义的非法字段 return schema.validate(student, { stripUnknown: true }) }
新增的stripUnknown: true配置会自动清除请求体中不在schema定义内的非法字段,避免脏数据写入数据库。
步骤2:修改接口内的校验和更新逻辑
router.patch("/:id", async (req, res) => { // 校验学生是否存在 let student = await Student.findById(req.params.id); if (!student) return res.status(400).send("Student Not Found"); // 校验请求参数,第二个参数传true标识为更新场景 const { error } = validateStudent(req.body, true); if (error) return res.status(400).send(error.details[0].message); // 执行更新 const updateResult = await Student.updateOne({_id:req.params.id},{$set: req.body}); // updateOne返回的是操作结果对象,通过matchedCount判断是否匹配到目标记录 if(updateResult.matchedCount === 0) return res.status(404).send('The student record not Updated') res.status(200).send('Student Record Updated') });
可选优化:直接获取更新后的文档
如果你需要接口返回更新后的学生数据,可以把updateOne替换为findByIdAndUpdate:
const updatedStudent = await Student.findByIdAndUpdate( req.params.id, { $set: req.body }, { new: true } // new: true 配置指定返回更新后的文档 ) res.status(200).send(updatedStudent)
内容的提问来源于stack exchange,提问作者Adil Ijaz
相关产品推荐
相关产品推荐

