我正在尝试调试node.js Express应用程序中的可怕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
在调用knox s3client的on响应回调之前,可能会在某处调用或触发response.writeHead
response.headersSent
。是否有一些调试标志或其他方式让Node在调用writeHead时吐出?节点0.10添加了{{1}},但是通过检查此标志以确定调用它的位置来填充我的代码和所有第三方库是非常困难的。还是有其他方法来解决这个问题?
答案 0 :(得分:12)
您可以尝试包含一个简单的中间件,在调用writeHead
时将堆栈跟踪转储到stdout:
app.use(function(req, res, next) {
res.on('header', function() {
console.trace('HEADERS GOING TO BE WRITTEN');
});
next();
});
您必须在可能触发问题的路由/中间件之前插入它。
FWIW,我的猜测是问题是由区域C中发生的事情触发的(res.send
,res.end
,res.json
或res.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
是正确的信息流,您可以试试这个:
function foo(req, res) {
global.s3client.get('foo').on('response', function(s3res) {
s3res.pipe(res);
}).end();
}
但请注意,S3请求返回的所有标头都将传递给Express响应。
答案 1 :(得分:4)
我不确定它是否适用于此,但在看到错误时我立即想到的是“模型层”中某处“回调逻辑”中的一个简单的非常简单的错误。在两个不同的实例中,我一直在摸不着这个问题,在两个实例中,事实证明我调用了一个回调(即最终会调用响应写入回调的回调)两次。这只会在我的模型层中出现错误时发生,因为它的代码如下:
if (err) cb(err) // note the missing "return" here
cb(null,result) // alternatively, also no "else"
如果我现在遇到这样的错误,我要做的第一件事就是检查是否为任何操作调用了两次响应写回调。只需要在区域A(启动Knox请求的范围)中的一个console.log
语句和区域C中的一个语句(响应写入回调)就足以至少消除这种可能性。
原则上,您的res
对象应该受到很好的保护。只有连接/表达中间件才能访问它,当然这个特定的请求处理程序。因此,只有有限数量的地方有能力写入标题。