错误:发送到客户端后无法设置标题

我是相当新的Node.js,我有一些问题。

我正在使用Node.js 4.10和Express 2.4.3。

当我尝试访问http://127.0.0.1:8888/auth/facebook时 ,我将被redirect到http://127.0.0.1:8888/auth/facebook_callback 。

然后我收到以下错误:

Error: Can't render headers after they are sent to the client. at ServerResponse.<anonymous> (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].<anonymous> (/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.<anonymous> (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.<anonymous> (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.<anonymous> (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.<anonymous> (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.<anonymous> (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.<anonymous> (/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); 

我可以知道我的代码有什么问题吗?

我真的很新鲜,所以很抱歉只是把代码放在这里。

谢谢大家提前。

Express中的res对象是Node.js的http.ServerResponse的子类( 读取http.js源文件 )。 您可以随时调用res.setHeader(name, value) ,直到调用res.writeHead(statusCode) 。 在writeHead之后,头文件被烘焙,你只能调用res.write(data) ,最后res.end(data)

错误“错误:发送后无法设置标题”。 意味着您已经处于“正文”或“已完成”状态,但某些function试图设置标题或状态码。 当你看到这个错误的时候,试着去寻找任何试图在一些正文已经被写入之后发送一个头文件的东西。 例如,查找意外调用两次的callback,或发送主体后发生的任何错误。

在你的情况,你叫res.redirect() ,导致响应成为完成。 然后你的代码抛出一个错误( res.reqnull )。 并且由于错误发生在你的实际function(req, res, next) (不在callbackfunction(req, res, next)内),Connect能够捕获它,然后尝试发送一个500错误页面。 但是由于头文件已经发送,Node.js的setHeader抛出了你所看到的错误。

Node.js / Express响应方法的完整列表以及何时必须调用它们:

回应必须在头上 ,仍然在

  1. res.writeContinue()
  2. res.statusCode = 404
  3. res.setHeader(name, value)
  4. res.getHeader(name)
  5. res.removeHeader(name)
  6. res.header(key[, val]) (仅限Express)
  7. res.charset = 'utf-8' (仅限Express;仅影响特定于Express的方法)
  8. res.contentType(type) (仅限Express)

回应必须在头上 ,成为身体

  1. res.writeHead(statusCode, [reasonPhrase], [headers])

反应可以在任何一个头/身体上,并保持在身体上

  1. res.write(chunk, encoding='utf8')

回应可以在任何一个头部或身体上 完成

  1. res.end([data], [encoding])

响应可以在任何头部/身体中,并保持当前状态:

  1. res.addTrailers(headers)

回应必须在头上,并成为完成

  1. return next([err]) (仅连接/ Express)
  2. 中间件function(req, res, next)任何exception(仅适用于Connect / Express)
  3. res.send(body|status[, headers|status[, status]]) (仅限Express)
  4. res.attachment(filename) (仅限快递)
  5. res.sendfile(path[, options[, callback]]) (仅限Express)
  6. res.json(obj[, headers|status[, status]]) (仅限Express)
  7. res.redirect(url[, status]) (仅限Express)
  8. res.cookie(name, val[, options]) (仅限Express)
  9. res.clearCookie(name[, options]) (仅限Express)
  10. res.render(view[, options[, fn]]) (仅限Express)
  11. res.partial(view[, options]) (仅限Express)

我也碰到了这个错误。 我想(希望)我已经把头围上了,想写在这里作为参考。

当你使用app.use方法添加中间件来连接或者expression (这是build立在连接上)的app.use ,你需要将项目追加到Server.prototype.stack中connect(至less在当前的npm install connect ,作为这篇文章的一个github)。 当服务器获取请求时,它会迭代堆栈,调用(request, response, next)方法。

问题是,如果在其中一个中间件项目中写入响应正文或头文件(它看起来像是/或者出于某种原因), 但是不调用response.end()并且调用next()核心Server.prototype.handle方法完成,它会注意到:

  1. 在堆栈中没有更多的项目,和/或
  2. response.headerSent是真的。

所以,它会抛出一个错误。 但它引发的错误只是这个基本的回应(从连接http.js源代码:

 res.statusCode = 404; res.setHeader('Content-Type', 'text/plain'); res.end('Cannot ' + req.method + ' ' + req.url); 

在那里,它调用res.setHeader('Content-Type', 'text/plain'); ,你可能已经在你的render方法中设置了, 而不调用response.end() ,如:

 response.setHeader("Content-Type", "text/html"); response.write("<p>Hello World</p>"); 

一切需要结构化的方式是这样的:

良好的中间件

 // middleware that does not modify the response body var doesNotModifyBody = function(request, response, next) { request.params = { a: "b" }; // calls next because it hasn't modified the header next(); }; // middleware that modify the response body var doesModifyBody = function(request, response, next) { response.setHeader("Content-Type", "text/html"); response.write("<p>Hello World</p>"); response.end(); // doesn't call next() }; app.use(doesNotModifyBody); app.use(doesModifyBody); 

有问题的中间件

 var problemMiddleware = function(request, response, next) { response.setHeader("Content-Type", "text/html"); response.write("<p>Hello World</p>"); next(); }; 

有问题的中间件设置响应头而不调用response.end()并调用next() ,这会使连接的服务器混淆。

我有这个相同的问题,并意识到这是因为我没有返回声明调用res.redirect,所以下一个函数也被立即调用之后:

 auth.annonymousOnly = function(req, res, next) { if (req.user) res.redirect('/'); next(); }; 

应该是:

 auth.annonymousOnly = function(req, res, next) { if (req.user) return res.redirect('/'); next(); }; 

很多人打这个错误。 这是asynchronous处理的一个混淆。 最有可能的一些代码是在第一个tick中设置标题,然后在未来的tick中运行asynchronouscallback。 在这之间,响应头部被发送,但是接下来的头部(比如30Xredirect)尝试添加额外的头部,但是由于响应头部已经被发送,所以太迟了。

我不确定究竟是什么原因导致你的错误,但看看任何callback作为潜在的调查领域。

一个简单的提示,以简化您的代码。 摆脱app.configure(),并直接调用app.use在您的顶级作用域。

另请参阅everyauth模块,其中包括Facebook和其他第三方身份validation提供程序。

在这个问题上,我一直耿耿于怀,因为在处理callback上犯了一个粗心的错误而发生了这件事。 非返回的callback导致响应被设置两次。

我的程序有一个validation请求和查询数据库的代码。 在validation是否有错误之后,我正在调用带有validation错误的index.js。 如果validation通过,它会继续,并成功/失败的数据库。

  var error = validateRequestDetails("create",queryReq); if (error) callback(error, null); else some code callback(null, success); 

发生什么事情是:Incasevalidation失败的callback被调用和响应得到设置。 但没有返回。 所以它仍然继续的方法去db和命中成功/失败。 它再次调用相同的callback,导致响应被设置两次。

所以解决方法很简单,您需要“回传”callback,以便方法不会继续执行,一旦发生错误,因此设置响应对象一次

  var error = validateRequestDetails("create",queryReq); if (error) callback(error, null); return; else some code callback(null, success); 

发送响应后通过语句时会出现这种types的错误。

例如:

 res.send("something response"); console.log("jhgfjhgsdhgfsdf"); console.log("sdgsdfhdgfdhgsdf"); res.send("sopmething response"); 

将会导致你所看到的错误,因为一旦响应被发送,下面的res.send将不会被执行。

如果你想做任何事情,你应该在发送回复之前做。

在我的情况下,这是一个304响应(caching),这是造成这个问题。

最简单的解决scheme

 app.disable('etag'); 

如果您想要更多控制,可以在这里select其他解

http://vlasenko.org/2011/10/12/expressconnect-static-set-last-modified-to-now-to-avoid-304-not-modified/

对于任何人来说,没有其他解决scheme的帮助,在我的情况下,这performance在一个处理图像上传,但没有处理超时的路线,因此,如果上传花了太长时间,当callback被解雇在发送超时响应之后 ,调用res.send()导致崩溃,因为头文件已经被设置为超时。

这很容易通过设置一个非常短的超时时间,并用一个非常大的图像击中路线,每次重现崩溃。

在我的情况下,这发生在React和postal.js,当我没有从我的React组件的componentWillUnmountcallback中取消订阅。

只是靠这个。 你可以通过这个函数传递响应:

 app.use(function(req,res,next){ var _send = res.send; var sent = false; res.send = function(data){ if(sent) return; _send.bind(res)(data); sent = true; }; next(); }); 

当响应发送给客户时,会发生这种情况,并且您正在尝试回复。 你必须检查你的代码,在某个地方,你正在返回响应客户端,导致这个错误。 当你想返回时,检查并返回一次响应。

有时,如果在res.endres.send之后尝试调用next()函数,可能会出现此错误,如果在函数中res.send或res.end之后有next(),则尝试删除。 注意:next()意味着在响应客户端响应( 即res.send或res.end )之后,你仍然试图执行一些代码来再次进行响应,这是不合法的。

例如:

 router.get('/',function (req,res,next){ res.send("request received"); next(); // this will give you the above exception }); 

从上面的函数中删除next() ,它将工作。

添加这个中间件,它会工作

 app.use(function(req,res,next){ var _send = res.send; var sent = false; res.send = function(data){ if(sent) return; _send.bind(res)(data); sent = true; }; next(); });