错误:无法在将头发送到客户端后设置头
错误:无法在将头发送到客户端后设置头
我对Node.js相当新,并且我遇到了一些问题。
我正在使用Node.js 4.10和Express 2.4.3。
当我尝试访问http://127.0.0.1:8888/auth/facebook时,我会被重定向到http://127.0.0.1:8888/auth/facebook_callback。
然后我收到以下错误:
Error: Can't render headers after they are sent to the client. at ServerResponse. (http.js:573:11) at ServerResponse._renderHeaders (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:64:25) at ServerResponse.writeHead (http.js:813:20) at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/auth.strategies/facebook.js:28:15 at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/index.js:113:13 at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/strategyExecutor.js:45:39) at [object Object].pass (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/authExecutionScope.js:32:3) at [object Object].halt (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/authExecutionScope.js:29:8) at [object Object].redirect (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/authExecutionScope.js:16:8) at [object Object]. (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/auth.strategies/facebook.js:77:15) Error: Can't set headers after they are sent. at ServerResponse. (http.js:527:11) at ServerResponse.setHeader (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:50:20) at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:162:13) at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:195:11) at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:150:23) at param (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:189:13) at pass (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:191:10) at Object.router [as handle] (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:197:6) at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:198:15) at Object.auth [as handle] (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/index.js:153:7) Error: Can't set headers after they are sent. at ServerResponse. (http.js:527:11) at ServerResponse.setHeader (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:50:20) at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:162:13) at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:207:9) at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:150:23) at param (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:189:13) at pass (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:191:10) at Object.router [as handle] (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/router.js:197:6) at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:198:15) at Object.auth [as handle] (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/index.js:153:7) Error: Can't set headers after they are sent. at ServerResponse. (http.js:527:11) at ServerResponse.setHeader (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:50:20) at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:162:13) at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:150:23) at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:207:9) at Object.auth [as handle] (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect-auth/lib/index.js:153:7) at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:198:15) at HTTPServer.handle (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:211:3) at Object.handle (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:105:14) at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:198:15) Error: Can't set headers after they are sent. at ServerResponse. (http.js:527:11) at ServerResponse.setHeader (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:50:20) at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:162:13) at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:150:23) at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:207:9) at HTTPServer.handle (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:211:3) at Object.handle (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:105:14) at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:198:15) at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/session.js:323:9 at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/session.js:338:9 node.js:134 throw e; // process.nextTick error, or 'error' event on first tick ^ Error: Can't set headers after they are sent. at ServerResponse. (http.js:527:11) at ServerResponse.setHeader (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/patch.js:50:20) at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:162:13) at next (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/http.js:207:9) at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/session.js:323:9 at /home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/session.js:338:9 at Array. (/home/eugene/public_html/all_things_node/projects/fb2/node_modules/connect/lib/middleware/session/memory.js:57:7) at EventEmitter._tickCallback (node.js:126:26)
以下是我的代码:
var fbId= "XXX"; var fbSecret= "XXXXXX"; var fbCallbackAddress= "http://127.0.0.1:8888/auth/facebook_callback" var cookieSecret = "node"; // enter a random hash for security var express= require('express'); var auth = require('connect-auth') var app = express.createServer(); app.configure(function(){ app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(express.cookieParser()); app.use(express.session({secret: cookieSecret})); app.use(auth([ auth.Facebook({ appId : fbId, appSecret: fbSecret, callback: fbCallbackAddress, scope: 'offline_access,email,user_about_me,user_activities,manage_pages,publish_stream', failedUri: '/noauth' }) ])); app.use(app.router); }); app.get('/auth/facebook', function(req, res) { req.authenticate("facebook", function(error, authenticated) { if (authenticated) { res.redirect("/great"); console.log("ok cool."); console.log(res['req']['session']); } }); }); app.get('/noauth', function(req, res) { console.log('Authentication Failed'); res.send('Authentication Failed'); }); app.get('/great', function( req, res) { res.send('Supercoolstuff'); }); app.listen(8888);
请问我的代码哪里出了问题?
这个问答中的一些答案是错误的。被接受的答案也不是很“实用”,所以我想发表一个能用更简单的语言解释问题的答案。我的答案将覆盖我看到反复发布的99%的错误。关于错误背后的实际原因,请查看被接受的答案。
HTTP使用一个需要一个响应的循环来处理请求。当客户端发送一个请求(比如POST或GET),服务器应该只向它发送一个响应。
这个错误信息:
Error: Can't set headers after they are sent.
通常发生在您为一个请求发送多个响应时。请确保以下功能每个请求只被调用一次:
res.json()
res.send()
res.redirect()
res.render()
(以及其他很少使用的功能,请查看接受的答案)
当调用这些res函数时,路由回调将不会返回。它将继续运行,直到达到函数的末尾或返回语句。如果您想要在发送响应时立即返回,可以像这样做:return res.send()
。
以这段代码为例:
app.post('/api/route1', function(req, res) { console.log('this ran'); res.status(200).json({ message: 'ok' }); console.log('this ran too'); res.status(200).json({ message: 'ok' }); }
当发送一个POST请求到/api/route1时,它将运行回调中的每一行。会抛出一个“不能在发送响应后设置header”的错误信息,因为调用了两次res.json()
,也就是发送了两个响应。
每个请求只能发送一个响应!
上述代码示例中的错误十分明显。一个更为常见的问题是当分支较多时:
app.get('/api/company/:companyId', function(req, res) { const { companyId } = req.params; Company.findById(companyId).exec((err, company) => { if (err) { res.status(500).json(err); } else if (!company) { res.status(404).json(); // This runs. } res.status(200).json(company); // This runs as well. }); }
这个与回调函数绑定的路由会在数据库中查找公司。当查询公司时发现其并不存在时,我们会进入else if
分支并发送一个404响应。随后,我们继续移动到下一条语句,该语句也会发送响应。现在,我们已经发送了两个响应,就会出现错误消息。我们可以通过确保仅发送一个响应来修复此代码:
.exec((err, company) => { if (err) { res.status(500).json(err); } else if (!company) { res.status(404).json(); // Only this runs. } else { res.status(200).json(company); } });
或者在发送响应时返回:
.exec((err, company) => { if (err) { return res.status(500).json(err); } else if (!company) { return res.status(404).json(); // Only this runs. } return res.status(200).json(company); });
异步函数是一个大罪魁。以此问题中的函数为例:
article.save(function(err, doc1) { if (err) { res.send(err); } else { User.findOneAndUpdate({ _id: req.user._id }, { $push: { article: doc._id } }) .exec(function(err, doc2) { if (err) res.send(err); else res.json(doc2); // Will be called second. }) res.json(doc1); // Will be called first. } });
在这个代码示例中,我们有一个异步函数(findOneAndUpdate()
)。如果没有错误(err
),findOneAndUpdate()
将被调用。由于这个函数是异步的,res.json(doc1)
将立即被调用。假设findOneAndUpdate()
没有错误。然后将调用else
中的res.json(doc2)
。现在已经发送了两个响应,就会出现无法设置标题的错误消息。
这种情况下的解决方法是删除res.json(doc1)
。为了将两个文档都发送回客户端,else
中的res.json()
可编写为res.json({ article: doc1, user: doc2 })
。