You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 19:12:01