在Express / Node.js中debugging“在发送后无法设置标头”错误的最佳方法?

我试图debugging可怕的Can't set headers after they are sent在node.js快递应用程序Can't set headers after they are sent错误。 具体来说,我正在使用knox库来与s3交谈。

粗略地说,我有这个Express处理程序,使用一个knox s3client的全局实例:

 function foo(req, res) { //Region A var s3req = global.s3client.get('foo').on('response', function(s3res){ //Region B res.set('content-length', s3res.headers['content-length']); //This will fail s3res.on('data', function(chunk){ res.write(chunk); }); }); //Region C s3req.end(); } 

如果我在区域A的res上设置任何标题或状态代码,一切工作正常。 如果我尝试在区域B中的任何一个,我得到“发送后无法设置标题”错误。 请注意,我想在res上设置标题,而不是s3res

据推测response.writeHead被调用或触发之前,在响应callback的knox s3client被调用。 是否有一些debugging标志或其他方式让节点吐出时/ writeHead被称为? 节点0.10增加了一个response.headersSent ,但是用这个标志检查来填充我的代码和所有的第三方库是非常困难的,以找出这个被调用的地方。 还是有其他的方法来解决这个问题?

您可以尝试包含一个简单的中间件,在writeHead时将堆栈跟踪转储到标准输出:

 app.use(function(req, res, next) { res.on('header', function() { console.trace('HEADERS GOING TO BE WRITTEN'); }); next(); }); 

你必须在可能触发你的问题的路由/中间件之前插入。

FWIW,我的猜测是这个问题是由C区域发生的事情触发的( res.sendres.endres.jsonres.render )。

 function foo(req, res) { //Region A var s3req = global.s3client.get('foo').on('response', function(s3res){ //Region B res.set('content-length', s3res.headers['content-length']); }); //Region C } 

编辑如果s3res是一个适当的stream,你可以试试这个:

 function foo(req, res) { global.s3client.get('foo').on('response', function(s3res) { s3res.pipe(res); }).end(); } 

但请注意,S3请求返回的所有头文件都将传递给Express响应。

我不确定它是否适用于这里,但是当我看到这个错误的时候,我立即想到的是在“模型层”某处的“callback逻辑”中的一个简单的非常简单的错误。 我曾经在两个不同的实例中头脑研究这个问题,在两种情况下,我都调用了一个callback函数(即最终会调用响应写callback函数的函数)两次。 这只会发生在我的模型层的错误,因为它是这样的代码:

 if (err) cb(err) // note the missing "return" here cb(null,result) // alternatively, also no "else" 

如果我现在遇到这样的错误,我做的第一件事就是检查是否为任何操作调用了响应写入callback两次。 只有两个console.log语句在区域A(Knox请求被启动的范围)和一个在区域C(响应写入callback)中的一个足以至less消除这种可能性。

原则上,你的res对象应该是相当的保护。 只有连接/expression中间件才能访问它,当然这个特殊的请求处理程序。 所以只有有限的几个地方有写头的权力。