节点,快速,案例研究:“错误:发送后无法设置标题”

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

Error: Can't set headers after they are sent. 

我无法重现错误。 我想我find了一个修复程序,但我想确保我理解下面代码中概述的问题。 基于这篇文章 ,我相信它来自我的Express路由器中的多个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(..)两次导致错误,那么为什么我不能重现这个错误? 我从来没有得到错误,但另一个用户做了。

任何人都可以提供解释吗? 对我的解决scheme的反馈也是非常有帮助的。

你应该重构你的代码,atm你有2个asynchronous调用你的代码

 db.collection('userlist').find is called and will return later db.collection('userlist').insert and will also return later 

所以你打电话res.send 2次

像这样的事情,你不会调用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}) }) }) 

您对问题的原因是正确的,但是,这里没有足够的信息来确定解决scheme是否正确。

例如, find / insert调用之间没有明显的关系 – 这些应该是串联调用吗? insert是否需要find任何东西? 例如,如果您插入之前需要检查一条logging那么正确的解决scheme看起来就像这样

 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'); } }); } }); 

注意这里的区别? 请记住, findinsert非阻塞调用,因此在您的示例中,您不能保证两个callback的运行顺序。 但是,通过将insert调用移动到findcallback函数中,我们保证在find 之后总会调用insert