简体   繁体   中英

Nodejs stream (Error: Can't set headers after they are sent)

I am trying to build an api from the braintree servers. Referring to this doc https://developers.braintreepayments.com/javascript+node/reference/general/result-handling/search-results

To access all the transactions from their server I have to return a node stream.

ex

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. 
      });

  }
});

I understand a stream returns data in chunks, so the res.json() above is most likely is being called multiple times resulting in Error: Can't set headers after they are sent .

So my question is how can I send that data to the client in one chunk? The nodejs streaming is confusing to me, I am going to read up more about it, but it would be great to understand how to send the data to the client without re-sending the headers.

You shouldn't make any assumptions about data events unless the stream you are reading from is in object mode. You could get one data event or a hundred (depending on the input size of course) because TCP is a stream.

What you probably want is something like this instead (assuming stream is not in object mode):

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);
      });

  }
});

Or just pipe the stream to the response:

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);
  }
});

For an object stream you might do:

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);
      });

  }
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM