Mongoose findOneAndUpdate未更新文档反而创建新文档求助
PUT路由未更新指定MongoDB文档,反而修改了其他用户的文档(Node/Express/Mongoose)
问题分析
你的核心问题出在**findOneAndUpdate的参数使用错误**,再加上缺少权限验证,导致出现了非预期的行为。
先看你的PUT路由代码:
// 此处id是评论的唯一ID,而非电影ID router.put('/update/:id', [jsonParser, jwtAuth], (req, res) => { Review.findOneAndUpdate(req.params.id, { $set: { ...req.body } }, { new: true }) .then(review => { console.log(review); res.status(203).json(review); }) .catch(err => res.status(500).json({message: err})); });
findOneAndUpdate的第一个参数要求是查询条件对象,但你直接传了req.params.id这个字符串。虽然部分Mongoose版本会自动把字符串解析为{ _id: 字符串 }的查询条件,但显然在你的环境里这个解析出了问题——它没有匹配到你指定的评论ID,反而意外匹配到了另一个用户的评论文档,然后更新了它。
另外,你的路由没有验证当前登录用户是否是评论的所有者,这也导致了可以随意修改不属于自己的评论。
解决方案
1. 修正查询参数,确保匹配目标文档
你有两种更可靠的写法:
写法一:使用findByIdAndUpdate(推荐,更直观)
这是Mongoose提供的专门通过ID查询并更新的方法,是findOneAndUpdate({ _id: id }, ...)的语法糖:
router.put('/update/:id', [jsonParser, jwtAuth], (req, res) => { Review.findByIdAndUpdate( req.params.id, { $set: { ...req.body } }, { new: true, runValidators: true } // 加上runValidators确保更新时触发模型验证 ) .then(review => { if (!review) { return res.status(404).json({ message: "目标评论不存在" }); } console.log(review); res.status(200).json(review); // 200是更新成功的标准状态码,203不适合这里 }) .catch(err => res.status(500).json({ message: err.message })); });
写法二:明确传入查询对象
如果你坚持用findOneAndUpdate,一定要把ID放在查询对象里:
router.put('/update/:id', [jsonParser, jwtAuth], (req, res) => { Review.findOneAndUpdate( { _id: req.params.id }, // 明确指定查询_id字段 { $set: { ...req.body } }, { new: true, runValidators: true } ) .then(review => { if (!review) { return res.status(404).json({ message: "目标评论不存在" }); } console.log(review); res.status(200).json(review); }) .catch(err => res.status(500).json({ message: err.message })); });
2. 添加权限验证,防止修改他人评论
既然你用了JWT认证,肯定能从req.user拿到当前登录用户的信息。你需要在查询条件里加上reviewer字段的匹配,确保只有评论的所有者才能修改它:
router.put('/update/:id', [jsonParser, jwtAuth], (req, res) => { Review.findOneAndUpdate( { _id: req.params.id, reviewer: req.user._id // 假设jwtAuth把用户ID存在req.user._id里 }, { $set: { ...req.body } }, { new: true, runValidators: true } ) .then(review => { if (!review) { return res.status(403).json({ message: "无权限修改该评论,或评论不存在" }); } console.log(review); res.status(200).json(review); }) .catch(err => res.status(500).json({ message: err.message })); });
额外说明
- 为什么之前会修改其他用户的文档?因为错误的查询参数没有匹配到目标ID,反而意外匹配到了某个现有文档(可能是Mongoose版本的解析bug,或者某个字段的值刚好和你传入的ID字符串一致)。
- 状态码建议:203是"非权威信息",不适合用于更新成功的响应,推荐用200(返回更新后的资源)或204(无内容)。
- 加上
runValidators: true可以确保更新操作时触发你在Review模型里定义的验证规则,避免脏数据。
内容的提问来源于stack exchange,提问作者Kaleidics
相关产品推荐
相关产品推荐

