Express.js路由错误:无法在发送后设置标头

26

我不太确定为什么会出现这个错误。这是一个基于express.js构建的简单API,可以添加和删除帖子。当我触发删除路由时出现了错误。我读过这种错误通常发生在有两个回调函数的情况下,但是我似乎找不到任何双重回调。

    _http_outgoing.js:344
    throw new Error('Can\'t set headers after they are sent.');
    Error: Can't set headers after they are sent.
    at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:344:11)
    at ServerResponse.header (/Users/bounty/Projects/_learning/react-express/node_modules/express/lib/response.js:718:10)
at ServerResponse.send (/Users/bounty/Projects/_learning/react-express/node_modules/express/lib/response.js:163:12)
    at ServerResponse.json (/Users/bounty/Projects/_learning/react-express/node_modules/express/lib/response.js:249:15)
    at /Users/bounty/Projects/_learning/react-express/server/routes/posts.js:86:9
    at nextTickCallbackWith0Args (node.js:452:9)
    at process._tickCallback (node.js:381:13)

这里是我的posts.js路由器:

module.exports = function(router) {

    var Post = require('../models/post.js');

    // middleware for the api requests
    router.use(function(req, res, next) {
        // do logging
        console.log('something is happening.');
        next(); // make sure we go to our next route and don't stop here
    });

    // test route to make sure everything is working (accessed at GET http://localhost:8080/api)

    router.get('/', function(req, res) {
        res.json({ message: 'hooray! welcome to our api!' });   
    });

    // all routes here

    // routes that end in /posts
    router.route('/posts')

        // create a Post (accessed at POST http://localhost:7777/api/posts)
        .post(function(req, res) {
            var post = new Post();
            post.postTitle = req.body.postTitle; // set the post name (comes from request) 

            // save post and check for errors
            post.save(function(err) {
                if (err)
                    res.send();

                res.json({ message: 'post created!' });
            });
        })

        // get all Posts (accessed at GET http://localhost:7777/api/posts)
        .get(function(req, res) {
            Post.find(function(err, posts) {
                if (err)
                    res.send();

                res.json(posts);
            });
        });

    // routes that end in /posts for specific id
    router.route('/posts/:post_id')

        // get the post with that id
        .get(function(req, res) {
            Post.findById(req.params.post_id, function(err, post) {
                if (err)
                    res.send(err);

                res.json(post);
            });
        })

        // update the post with that id
        .put(function(req, res) {
            Post.findById(req.params.post_id, function(err, post) {
                if (err)
                    res.send(err);

                post.postTitle = req.body.postTitle;

                // save the post
                post.save(function(err) {
                    if (err)
                        res.send(err);

                    res.json({ message: 'post updated!' });
                });
            });
        })

        // deletes the post with that id
        .delete(function(req, res) {
            Post.remove({
                _id: req.params.post_id
            }, function(err, post) {
                if (err) {
                    res.send(err);
                }
                res.json({ message: 'post deleted!' });
            });
        });
}

post.save()post.find() 实际上是做什么的?这是你的数据库吗? - jfriend00
@jfriend00 是的,save() 将帖子存入数据库。find() 从数据库获取帖子。该数据库是 MongoDB。 - bounty
7个回答

71

你需要添加'return',这样才不会出现重复回复的问题。

// save post and check for errors
post.save(function(err) {
    if (err) {
        return res.send();
    }
    res.json({ message: 'post created!' });
});

谢谢!这个答案解决了问题的一部分。我已经为此苦苦挣扎了几个小时。 - AllJs
编写中间件时,请注意多次调用next()。 - Michael Ribbons
这总是这样吗?res语句必须始终返回吗? - softcode
我认为这是因为,如果出现错误,将会调用res.send(),然后是res.json({ message: 'post created!' });return的作用类似于一个跳出器,请注意if语句没有else - Coyolero
你可以使用if/else语句来替代if语句。 - Isaac Pak

17

那个特定的错误消息几乎总是由于异步响应处理中的时间错误引起,这会导致您尝试在响应已经发送后发送数据。

当人们将 Express 路由内部的异步响应视为同步响应时,通常会发生这种情况,并最终发送两次数据。


我看到您可能会在任何一个错误路径上遇到这个问题:

当您执行以下操作时:

       // save post and check for errors
        post.save(function(err) {
            if (err)
                res.send();

            res.json({ message: 'post created!' });
        });
如果 post.save() 出现错误,您需要执行 res.send(),然后执行 res.json(...)。 您的代码需要有一个 returnelse 语句,以便在出现错误时不会同时执行两个代码路径。

6
因此,在尝试两次发送res.end时,Express会发生这种情况,而res.sendres.json都会这样做。在你的if(err)块中,你需要将return res.send()作为res.send是异步运行的,而res.json也被调用了。我想知道你的delete路由是否出错了?希望这能帮到你。

最好!


4
您正在同一次请求中两次使用res.send()res.json(),这会先发送头部信息,接着是响应体,然后又发送头部信息。req.next通常不是一个函数,而是通过第三个参数传递给中间件的。如果您想要跳转到下一个中间件,请使用该参数。(假设您正在使用Express框架)

1

为了完整起见,我也会提到:

有时问题可能在中间件上,你可能通过调用app.use来使用它。

在检查前面答案提到的明显错误后:

您应该删除所有app.use语句,然后逐个重新引入它们,以找到有问题的模块。


0

如果想要快速解决问题,您可以在调用res.send()之前检查res.finished

if (!res.finished)
    res.send()

-1
    If you are using res.send() inside any loop, then you need to break it after the use of res.send(). So that it won't allow resetting of the res headers again and again. 
    for e.g : 
    for(){
if(){
res.send();
break;
}
else(){
res.send();
break;
}    
    }
In my case this is the problem and I solved it like this.
    Hope it may help someone in future.
    Thanks

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接