【问题标题】:Node express error "can't set headers after they are sent"节点快递错误“发送后无法设置标头”
【发布时间】:2016-04-18 21:22:34
【问题描述】:

我已阅读有关此错误的其他问题,但我看不出任何答案如何适用于我自己的案例。我已经检查了通常的罪魁祸首——在调用 send 后我没有对响应对象做任何事情,据我所知,我的回调不会被多次调用。但无论我尝试什么,我都会收到错误“发送后无法设置标题”。这是我的代码示例:

var express = require('express');
var ws = require('nodejs-websocket');
var app = express();
var socket = ws.connect("ws://localhost:5000");

var port = 8000;

app.get('/', function(req, res) {
  var r = JSON.stringify({
    type: "page request",
    request: {
      url: req.query.url,
      app: req.query.app
    }
  });
  socket.on("text", function(str){
    console.log(str);
    var d = JSON.parse(str);
    res.send(d.response.body);
    return;
  });
  socket.sendText(r);
});

app.listen(port, function(){
  console.log("Node app is running on port", port);
});

对“/”的第一个请求就像一个魅力(或者看起来如此)。但是,每次第二个请求都会导致“发送后无法设置标头”。也许我不明白 websocket 的回调是如何围绕 res 对象关闭的?它是否一直在引用原始 res 对象?我原以为每次请求路由时都会使用新的 res 对象重新定义回调,但也许我错了?

这里还有其他问题。例如,如果没有从 websocket 收到响应,那么响应将永远不会被返回,这是我计划解决的问题。此外,代码现在的方式通过 websocket 传递的任何第二条消息都可能导致错误,但我目前正在我的本地机器上仅使用一个连接的客户端进行测试,并且我正在将所有消息打印到控制台并且可以看到在请求/响应生命周期中只有一条消息通过。非常感谢任何帮助!

【问题讨论】:

  • 您是否搜索过此特定错误?这里可能有 100 个与该特定错误相关的类似问题,并且根本原因始终相同。从代码的外观来看,您还需要了解 异步 响应。您的代码不会从上到下执行。异步回调在很久以后才被调用。
  • 而且,您不希望在 app.get() 处理程序中执行 socket.on()。每次命中路由时都会添加一个新的事件处理程序。这段代码有很多问题。我们真的无法帮助重新设计它,因为您没有描述所需的行为。
  • @jfriend00 - 那么这就回答了这个问题 - 所以调用 socket.on("text"... 定义了第二个处理程序?它不会覆盖第一个处理程序?这显然可以解释它。而且我知道代码不会从上到下执行,我不希望它这样做。
  • 您还存在并发问题,因为如果多个请求到达您的服务器到 / 路由,您无法分清哪个 socket.on('text') 响应属于哪个传入的 http 请求,他们可以轻松混在一起。设计需要一些工作。

标签: node.js express websocket


【解决方案1】:

这里有多个问题。

第二个请求的问题是socket.on(...)每次调用时都会添加一个新的监听器。因此,每个app.get(),您添加另一个socket.on() 侦听器,这样它们就会堆积起来,并且您会为每条传入消息获得多个调用,从而导致多次尝试发送响应,从而导致特定的错误消息你看。几乎从来没有在请求处理程序中添加侦听器的情况,因为每次处理请求时它们都会累积。

此外,这里还有一个并发问题。如果您同时有多个app.get('/', ...) 请求在处理中,则您无法确定哪个socket.io 响应与哪个Web 请求对应,因此它们很可能会混淆。您可能需要标记每个套接字请求,以便当响应返回时,您知道它属于哪个请求,并且可以将响应发送到正确的请求。

【讨论】:

  • 谢谢,这就是问题所在。我错误地假设它正在重写侦听器,而不是添加一个新侦听器。我只是在回调中添加了一行来调用 socket.removeListener,这确保了只有一个事件侦听器。谢谢!
  • @h2ounderthebridge - 我不知道您是如何尝试修复代码的,但您也可能遇到了并发问题,我已将其添加到我的答案中。
  • 感谢您的意见。我所做的是将回调分配给一个名为 c 的变量,然后在回调中调用“socket.removeListener('text', c)”在响应返回后将其删除。这样,每次都会生成一个仅响应该特定连接的回调。此外,这是一个单用户(我)应用程序,永远不会有并发请求。我知道如果有多个用户访问服务器,这将是一个坏主意。
猜你喜欢
  • 2016-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多