11

我正在尝试调试Can't set headers after they are sentnode.js Express 应用程序中的可怕错误。具体来说,我正在使用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是在调用 knox s3client 的响应回调之前在某处被调用或触发。是否有一些调试标志或其他方法可以让 Node 在调用 writeHead 时/在哪里吐出?节点 0.10 添加了一个response.headersSent,但是通过检查此标志来填充我的代码和所有 3rd 方库以找出调用它的位置将非常困难。还是有其他方法可以解决这个问题?

4

2 回答 2

12

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

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

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

FWIW,我的猜测是问题是由区域 C 中发生的某些事情触发的(a res.sendres.endres.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 响应。

于 2013-05-01T06:25:04.260 回答
4

我不确定它是否适用于这里,但是当我看到错误时我立即想到的是“模型层”某处的“回调逻辑”中的一个简单非常简单的错误。在两个单独的实例中,我一直对此感到头疼,在这两个实例中,结果证明我调用了回调(即最终会调用响应写入回调的回调)两次。这只会在我的模型层出现错误时发生,因为它的代码如下:

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

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

原则上,您的res对象应该受到很好的保护。只有 connect / express 中间件可以访问它,当然还有这个特定的请求处理程序。因此,只有有限数量的地方具有写入标题的能力。

于 2013-05-01T09:10:47.750 回答