Node.js 节点、快车、案例研究:“;错误:Can';t在发送后设置标题";

Node.js 节点、快车、案例研究:“;错误:Can';t在发送后设置标题";,node.js,mongodb,asynchronous,express,Node.js,Mongodb,Asynchronous,Express,我的服务器崩溃,我在日志中发现以下错误: Error: Can't set headers after they are sent. 我无法重现这个错误。我想我找到了一个解决方案,但我想确保我理解这个问题,下面的代码中概述了这个问题。基于,我相信它来自我的Express router中的多个res.send(..)呼叫: router.post('/adduser', function(req, res) { var msg = '(message okay)' var

我的服务器崩溃,我在日志中发现以下错误:

   Error: Can't set headers after they are sent.
我无法重现这个错误。我想我找到了一个解决方案,但我想确保我理解这个问题,下面的代码中概述了这个问题。基于,我相信它来自我的Express router中的多个
res.send(..)
呼叫:

router.post('/adduser', function(req, res) {
    var msg = '(message okay)'
    var db = req.db;    
    db.collection('userlist').find({
        username: req.body.username;
    }).toArray(function(err, items) {

        // verify the user, set session fields, etc...

        console.log("message after user verification is: ");
        console.log(msg);

        res.send({
            msg: msg
        });

    });

    // Insert the request's data into the 'userlist' collection.
    db.collection('userlist').insert(body, function(err, result) {
        console.log("inserting into userlist...");
        res.send(
            (err === null) ? {
                msg: msg
            } : {
                msg: err
            }
        );
    });
    console.log("message after user verification is: ");
    console.log(msg);
}
我的日志的最后几行是:

message after user verification is: 
(message okay)
POST /login/adduser 200 7ms - 10b
inserting into userlist...

/home/lucas/mynode/node_modules/mongoskin/node_modules/mongodb/lib/mongodb/c
onnection/base.js:245
        throw message;      
              ^

Error: Can't set headers after they are sent.
 at ServerResponse.OutgoingMessage.setHeader (http.js:689:11)
 at ServerResponse.header (/home/lucas/mynode/node_modules/express/lib/response.js:717:10)
 at ServerResponse.send 
我怀疑我调用了
res.send(..)
两次,所以我相信我通过删除第一个
res.send(..)
解决了这个问题,但这是正确的解决方法吗?另外,如果调用
res.send(..)
两次导致错误,那么为什么我不能重现此错误?我从未收到错误,但另一个用户收到了


有人能解释一下吗?对我的解决方案的反馈也会非常有用。

您应该重新构造代码,因为您的代码中有两个异步调用

db.collection('userlist').find is called and will return later
db.collection('userlist').insert and will also return later
所以你给res.send打了两次电话

像这样的东西,你不会再给res.send打两次电话

router.post('/adduser', function(req, res) {
  var msg = '(message okay)'
  var db = req.db;

  db.collection('userlist').find({username: req.body.username}).toArray(function(err, items) {
    // verify the user, set session fields, etc...
    // if error return(send) error
    if(err) return res.send({err:"errormessage"})
    // if everything is ok - Insert the request's data into the 'userlist' collection.
    db.collection('userlist').insert(body, function(err, result) {
      //if error return(send)  error
      if(err) return res.send({err:err})
      //everything was ok send msg
      res.send({msg:msg})
    })
  })

您应该重新构造代码,因为您的代码中有两个异步调用

db.collection('userlist').find is called and will return later
db.collection('userlist').insert and will also return later
所以你给res.send打了两次电话

像这样的东西,你不会再给res.send打两次电话

router.post('/adduser', function(req, res) {
  var msg = '(message okay)'
  var db = req.db;

  db.collection('userlist').find({username: req.body.username}).toArray(function(err, items) {
    // verify the user, set session fields, etc...
    // if error return(send) error
    if(err) return res.send({err:"errormessage"})
    // if everything is ok - Insert the request's data into the 'userlist' collection.
    db.collection('userlist').insert(body, function(err, result) {
      //if error return(send)  error
      if(err) return res.send({err:err})
      //everything was ok send msg
      res.send({msg:msg})
    })
  })
关于问题的原因,您是正确的,但是,这里没有足够的信息来确定解决方案是否正确

例如,示例中的
find
/
insert
调用之间没有明显的关系-这些调用是否应该串联调用?
insert
是否需要
查找中的任何内容?例如,如果需要在插入之前检查记录是否存在,则正确的解决方案如下所示

db.collection('userlist').findOne({
    username: req.body.username
}, function(err, result) {
    if (result !== null) {
        db.collection('userlist').update(result, body, function(err, updateCount) {
            if (err !== null) {
                console.log('Updated existing record');
            }
        });
    } else {
        db.collection('userlist').insert(body, function(err, result) {
            if (err !== null) {
                console.log('Created new record');
            }
        });
    }
});
注意这里的区别吗? 请记住,
find
insert
是非阻塞调用,因此在您的示例中,您无法保证两个回调的运行顺序。但是,通过将
insert
调用移动到
find
回调中,我们可以保证
insert
将始终在
find
之后调用,您对问题原因的判断是正确的,但是,这里没有足够的信息来确定解决方案是否正确

例如,示例中的
find
/
insert
调用之间没有明显的关系-这些调用是否应该串联调用?
insert
是否需要
查找中的任何内容?例如,如果需要在插入之前检查记录是否存在,则正确的解决方案如下所示

db.collection('userlist').findOne({
    username: req.body.username
}, function(err, result) {
    if (result !== null) {
        db.collection('userlist').update(result, body, function(err, updateCount) {
            if (err !== null) {
                console.log('Updated existing record');
            }
        });
    } else {
        db.collection('userlist').insert(body, function(err, result) {
            if (err !== null) {
                console.log('Created new record');
            }
        });
    }
});
注意这里的区别吗?
请记住,
find
insert
是非阻塞调用,因此在您的示例中,您无法保证两个回调的运行顺序。但是,通过将
insert
调用移动到
find
回调中,我们可以保证
insert
将始终在
find
之后调用。是的,这是由于多次使用
res.send()
。Express'
res.send()
通过完成对web客户端的响应。一旦响应结束,在新的
req
及其自身的
res
到达之前,不应该/可以写入进一步的输出。注意,两个异步操作之间可能也存在竞争条件,并且无法保证顺序。如果要确保第二个db操作发生在第一个db操作之后,则需要手动排序,在第一个db操作完成之前不启动第二个db操作,或者在第一个db操作完成之前不插入第二个db操作的结果。最近,我也遇到了同样的问题。我使用了res.set(“连接”、“关闭”);在res.json()之后。。。这就是问题所在。我删除了res.set和wolla,它工作得很好。是的,这是由于多次使用
res.send()
。Express'
res.send()
通过完成对web客户端的响应。一旦响应结束,在新的
req
及其自身的
res
到达之前,不应该/可以写入进一步的输出。注意,两个异步操作之间可能也存在竞争条件,并且无法保证顺序。如果要确保第二个db操作发生在第一个db操作之后,则需要手动排序,在第一个db操作完成之前不启动第二个db操作,或者在第一个db操作完成之前不插入第二个db操作的结果。最近,我也遇到了同样的问题。我使用了res.set(“连接”、“关闭”);在res.json()之后。。。这就是问题所在。我移除了res.set和wolla,它工作得很好。