【问题标题】:Express.js: Headers already sent on REST APIExpress.js:已在 REST API 上发送的标头
【发布时间】:2016-09-26 17:50:10
【问题描述】:

我使用 Node.js 和 Express.js 创建了一个 REST API。 在一个 URL 上,当我再次调用此 URL 时收到一条错误消息:

发送 json 错误:[错误:发送后无法设置标头。]

所以,我添加了一个 try catch 来捕获错误。但奇怪的是,这项工作很好。错误被捕获并发送我的响应:

try {
  res.status(200).json(user);
} catch (e) {
  console.log('send json error:',e);
}

res.on('finish', function() {
  console.log('All is ok, response sent with user datas');
});

我认为这不是很干净的解决方案。所以我想知道为什么会抛出这个错误以及如何为这个问题编写一个干净的解决方案?

【问题讨论】:

  • 你能分享你遇到这个问题的完整代码吗?
  • 你能显示你的原始代码吗?可能的答案是,在您的错误处理程序已经在响应流中发送某些内容之后,您正在一个处理程序中调用 res.json()
  • @Paul:这里的代码很长:jsfiddle.net/120sav99
  • 你应该以模块化形式编写代码,它会在调试过程中产生问题。尝试将其分解为小函数
  • 您的代码看起来不错。尝试添加导致此异常的确切代码并添加异常日志(带有行号)。它可能有助于调试。

标签: javascript node.js http express http-headers


【解决方案1】:

所以从您的 JSFiddle 中,我不得不说您可以做很多事情来模块化/清理您的代码。现在很难弄清楚在哪里发生了什么。虽然对闭包使用胖箭头表示法很方便,但当您有很多顺序或并行的闭包时,由于它们是完全匿名的,因此很难调试。

您也没有在任何地方指出其他中间件沿该路线触发,这可能是多次响应尝试的来源。

说了这么多,首先要做的事情非常简单:在每次调用res.send()res.end() 之前添加一个return 语句。每当你调用其中一个时,你希望你当前的函数停止做任何事情,所以要明确这一点,很多这类问题都会消失。

其次,为代码中的每个 Promise 处理程序或回调添加命名函数。这至少会为您提供更清晰的堆栈跟踪,并使您的代码对其他维护者更直观。

第三,考虑以您可以按名称要求的逻辑方式将这些函数分组到其他文件中。这将进一步阐明您的堆栈跟踪(通过为您提供文件名和行号来查看)。

最后,认真地安装和使用 node-inspector 或类似的调试工具,设置断点并单步调试您的代码。

// 在下面的 OP 评论后编辑

我不同意代码组织不是帖子的主要问题。这绝对导致了主要问题。您的评论(实际上应该在原始问题中)表明有问题的关闭被触发了两次。该闭包是 global 对象的事件处理程序(坏主意),该对象在加载用户数据时触发事件,但是您将当前处理程序的范围向下推入其中(响应对象),这是您在按照自己的方式组织代码时遇到的常见问题(范围出血)。

通过更好的模块化/结构,这些问题会更快地变得更加明显。您的建议是解决问题的技巧;真正的解决方案是更好地了解您的应用程序中发生的事情以及原因。

在这种情况下,请查看为什么该用户数据加载事件会多次触发。我猜它不应该是(至少不在同一个请求的范围内),但如果它应该是并且没关系,那么使用正确的承诺链行为只处理一次(无论是第一次还是最后一次取决于您的需要),并且仅在您真正准备好时才致电res.json()(并且再次,仅一次)。

【讨论】:

  • 所有优点。我个人建议 OP 学习如何正确使用 Promise,尤其是 Promise 链,因为当前的代码基本上是回调地狱使用 Promises。
  • 感谢您的回复。我知道我的代码需要大规模重构,但这不是本主题的主要问题:) 在 JSFiddle 中,返回错误的响应位于第 46 行。我第一次使用此函数时,一切正常,不是任何错误。但是第二次,try/catch 捕捉到错误“headers already sent”,但响应仍然发送。我不明白为什么以及如何可能。我发现这个:headersSent。也许可以解决这个问题?
  • @robertklep 是的,这就是为什么我倾向于与回答“我该怎么做?”的人辩论的原因。使用“使用 Promises,它们比回调更好”。事实是,你可以用任何东西写出糟糕的代码,你也可以用(几乎)任何东西写出好的代码。
  • @Paul:在任何情况下,Promise 或 Callbacks 都不是这里的问题。请记住原来的问题;)
  • @Nowis 在我的回答中,我从未说过任何关于承诺与回调的事情。我的评论是对 robertklep 的(正确的)观点的回应,即你可以做出承诺地狱,就像你可以让回调地狱一样。我对承诺没有异议;只是因为它们是糟糕代码的灵丹妙药。
猜你喜欢
  • 1970-01-01
  • 2016-05-14
  • 2016-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多