如何在node.js中设置两次res头文件时处理错误

时间:2016-12-25 17:13:27

标签: node.js express asynchronous response-headers

我对节点的req, res参数以及在使用异步调用时处理这些参数的最佳方法感到困惑。我目前拥有的一个函数应该是将一个Item添加到我的数据库中,相应地更新一些数据库模型,然后发送一个响应,说明更新是成功的。但是,如果发生错误,此函数异步调用的函数可能会发送错误的响应。如果发生这种情况,我会收到错误Can't set headers after they are sent,因为我试图拨打res.send两次。非常感谢某人帮助找出处理错误的更优化方法。谢谢!

主要功能:

item.author = req.user._id;
item.description = req.query.description;
item.rating = req.query.rating;

item.save()
    .then(result => {
        // update user's items
        UserController.updateItems(req.user._id, result._id);
        ItemController.updateItemRating(req.query.outingId, req.query.rating, res);
        res.send(result);
    })
    .catch(error => {
        res.send(error);
    });

updateItemRating:

export const updateItemRating = (itemId, rating, res) => {
    Item.findOne({ _id: itemId }).exec((err, item) => {
        if (item === undefined || item === null) {
            return res.status(404).send('Item not found; check item ID');
        }

        Item.findOneAndUpdate(
            { _id: itemId },
            { $set: { rating: rating },
            },
            (error, item) => {
                if (error) {
                    res.status(404).send('Error updating item with new rating');
                }
            });
    });
};

updateItems:

export const updateItems = (userId, itemId) => {
    User.findOneAndUpdate(
        { _id: userId },
        { $push: { items: [itemId] } },
        (err, user) => {
            console.log('user' + user);
            if (err) {
                console.log('got an error in updateItems');
            }
        });
};

2 个答案:

答案 0 :(得分:2)

updateItemsupdateItemRating的函数调用都是异步的。响应发送被多次调用,也不确定首先调用哪个方法发送。为了解决您的问题,我建议您应用以下技术:

  1. 回调:您可以将回调作为参数传递,这将执行res.send和您可以在错误或成功条件下调用的相同回调。

    UserController.updateItems(req.user._id, result._id,function(status,message){res.status(status).send(message);});

  2. 您可以更新项目评级方法,如:

    export const updateItemRating = (itemId, rating, callback) => {
        Item.findOne({ _id: itemId }).exec((err, item) => {
            if (item === undefined || item === null) {
                callback(404,'Item not found; check item ID');    
            }    
            Item.findOneAndUpdate(
                { _id: itemId },
                { $set: { rating: rating },
                },
                (error, item) => {
                    if (error) {
                         callback(404,'Error updating item with new rating'); 
                    }else{
                        callback(200);
                    }
                });
        });
    };
    
    1. Async Module:您可以使用此模块同步方法调用。

答案 1 :(得分:0)

而不是让你的更新函数发送结果,你应该抛出()一个错误,这样你的外部函数将捕获错误,你可以用它来返回它。

考虑这个问题的另一种方法是你的外部函数用res.send()处理成功案例,所以它也应该负责错误案例的res.send。

数据库层对调用者的了解越少,重用代码就越容易。

创建自定义错误类型以封装404:

function NotFoundError(message) {
  this.message = (message || "");
}
NotFoundError.prototype = new Error();

然后在你的内部函数中使用它:

export const updateItemRating = (itemId, rating, res) => {
Item.findOne({ _id: itemId }).exec((err, item) => {
    if (item === undefined || item === null) {
        throw new NotFoundError('Item not found; check item ID');
    }

    Item.findOneAndUpdate(
        { _id: itemId },
        { $set: { rating: rating },
        },
        (error, item) => {
            if (error) {
                throw new NotFoundError('Error updating item with new rating');
            }
        });
    });
};

你的主要成为:

item.save()
.then(result => {
    // update user's items
    UserController.updateItems(req.user._id, result._id);
    ItemController.updateItemRating(req.query.outingId, req.query.rating, res);
    res.send(result);
})
.catch(error => {
    if (error instanceof NotFoundError) {
       res.status(404).send(error.message);
    }
    else {
       res.send(error);
    }
});