Mongoose更新数组后调用save方法无法保存记录问题求助
问题排查与解决方案
核心问题定位
1. 响应发送后未终止代码执行
你判断用户已点赞后调用了res.status(409).send(),但没有添加return终止后续逻辑,代码会继续执行点赞操作、再次尝试发送响应,触发「无法在响应已发送后设置请求头」的异常,该异常被外层catch捕获,导致误返回404。
2. Array.push()用法错误
Array.push()的返回值是修改后数组的长度,不是修改后的数组本身。你写的topic.set({upvotes: topic.upvotes.push(reqId)})实际是将upvotes字段设置为数字类型,和Schema中定义的数组类型冲突,触发Mongoose数据校验错误,导致save失败。
3. 错误处理逻辑错位
Mongoose的findById在没有匹配文档时只会返回null,不会抛出异常。你当前外层catch捕获的不是文档不存在的错误,而是后续代码执行抛出的其他异常,错误处理逻辑完全不符合预期。
修正后代码
router.post('/upvote/:id', auth, async (req, res, next) => { try { const topic = await Topic.findById(req.params.id); // 先判断文档是否存在 if (!topic) { return res.status(404).send('Topic with given ID not found.'); } const reqId = req.body._id; if(topic.upvotes.includes(reqId)){ // 发送响应后直接return终止执行 return res.status(409).send('Topic already upvoted.'); } // 直接修改数组即可,不需要用set赋值push的返回值 topic.upvotes.push(reqId); // 标记数组字段变更,适配Mongoose对非基本类型字段的检测 topic.markModified('upvotes'); const savedTopic = await topic.save(); return res.status(201).send(savedTopic); } catch (err) { // 非业务逻辑错误走全局错误处理 next(err); } });
其他优化建议
如果你不需要拿到修改前的topic对象做校验,可以直接用原子操作findByIdAndUpdate配合$addToSet实现点赞,性能更高且无需手动处理数组修改和save逻辑:
const updatedTopic = await Topic.findByIdAndUpdate( req.params.id, { $addToSet: { upvotes: reqId } }, // $addToSet自动避免重复添加 { new: true, runValidators: true } // new返回修改后文档,runValidators开启校验 );
内容的提问来源于stack exchange,提问作者William
相关产品推荐
相关产品推荐

