【问题标题】:Nodejs stream (Error: Can't set headers after they are sent)Nodejs流(错误:发送后无法设置标头)
【发布时间】:2014-10-24 21:52:23
【问题描述】:

我正在尝试从 Braintree 服务器构建一个 api。参考这个文档https://developers.braintreepayments.com/javascript+node/reference/general/result-handling/search-results

要从他们的服务器访问所有交易,我必须返回一个节点流。

app.get('/project', function(req, res) {
  if(req.user) {

      var stream = gateway.transaction.search(function (search) {
        search.customerId().is(req.user.id);
      });

      stream.on("ready", function () {
        console.log(stream.searchResponse);
      });

      stream.on("data", function (data) {
        res.json(data) // can't set headers after they are sent. 
      });

  }
});

我了解流以块的形式返回数据,因此上面的 res.json() 很可能被多次调用,从而导致 Error: Can't set headers after they are sent

所以我的问题是如何将这些数据以一个块的形式发送给客户端? nodejs 流让我感到困惑,我将阅读更多关于它的内容,但如果了解如何在不重新发送标头的情况下将数据发送到客户端,那就太好了。

【问题讨论】:

  • res.json() 旨在用作单个响应并结束响应。在同一个请求/响应中不能多次调用它。它通常也不能用于任意数据块,因为它需要完全形成的 javascript 数据才能呈现合法的 JSON。
  • 是的,我在一定程度上理解了这一点,但我的问题是如何在 .on('data') 回调中获取该数据并将该数据发送到客户端。也许我应该提高我的香草 javascript 技能并从回调或其他东西中构建一个函数,然后在流 .on('end') 回调中使用 res.json()
  • 另外,您真的想将 JSON 发送到客户端还是只想将结果流式传输到客户端?我不知道你实际上想要完成什么。
  • 好吧,我的应用程序有一个部分,客户可以在其中查看他们的交易,所以我正在构建一个模型,或者可能是客户端 JavaScript 上的一个集合。所以我需要发出一个ajax请求,因此需要json。所以要回答你的问题,我怀疑如果数据是流式传输的,客户端 fetch 方法会很好用吗?
  • 我希望 Braintree 不会让它成为流,如果数据会很大,我会得到它,但他们在这里有一个完美的方法 gateway.transaction.find("theTransactionId", function (err, transaction) { }); 但我不能使用我需要的 transactonId按 customerId 搜索

标签: javascript node.js stream


【解决方案1】:

您不应该对data 事件做出任何假设,除非您从中读取的流处于对象模式。你可以得到一个 data 事件或一百个(当然取决于输入大小),因为 TCP 是一个流。

您可能想要的是这样的东西(假设stream 不在对象模式下):

app.get('/project', function(req, res) {
  if(req.user) {

      var stream = gateway.transaction.search(function (search) {
        search.customerId().is(req.user.id);
      });

      stream.on("ready", function () {
        console.log(stream.searchResponse);
      });

      var buf = '';
      stream.on("data", function (data) {
        buf += data;
      });

      stream.on("end", function() {
        res.setHeader('Content-Type', 'application/json');
        res.send(buf);
      });

  }
});

或者只是将流通过管道传递给响应:

app.get('/project', function(req, res) {
  if(req.user) {

      var stream = gateway.transaction.search(function (search) {
        search.customerId().is(req.user.id);
      });

      stream.on("ready", function () {
        console.log(stream.searchResponse);
      });

      res.setHeader('Content-Type', 'application/json');

      stream.pipe(res);
  }
});

对于对象流,您可能会这样做:

app.get('/project', function(req, res) {
  if(req.user) {

      var stream = gateway.transaction.search(function (search) {
        search.customerId().is(req.user.id);
      });

      stream.on("ready", function () {
        console.log(stream.searchResponse);
      });

      var result = [];
      stream.on("data", function (data) {
        result.push(data);
      });

      stream.on("end", function() {
        res.json(result);
      });

  }
});

【讨论】:

  • 好东西,我一直看到管道,但这确实有助于将其置于上下文中。有道理,我试试看。
  • 第一种方法似乎有效,第二种方法出现错误。我确实喜欢第一种方法,但我得到了[object Object][object Object],所以显然在 Braintree 服务器上有两个对象(事务)。那么如何将它们作为对象数组返回,而不是 [object Object][object Object] 这是一个菜鸟问题,但我的香草 javascript 可以使用一些工作。
  • 我复制了这里发生的事情jsfiddle.net/b30c7ady我认为这与正在发生的事情非常相似。我可能需要专门为此提出一个新问题,但我想知道为什么我会收到[object Object]。至少我可以搞定小提琴,而且我不必担心一遍又一遍地重新启动我的服务器并重新登录,所以这很好。
  • 好的,那么它看起来像是一个对象流。我已经更新了我的答案。
  • 啊,好吧,哇,我没想到要使用push。谢谢你帮了大忙。
猜你喜欢
  • 2017-12-22
  • 2018-09-02
  • 2023-04-01
  • 2017-03-03
  • 2019-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多