30

I'm trying to implement a simple HTTP endpoint for an application written in node.js. I've created the HTTP server, but now I'm stuck on reading the request content body:

http.createServer(function(r, s) {
    console.log(r.method, r.url, r.headers);
    console.log(r.read());
    s.write("OK"); 
    s.end(); 
}).listen(42646);

Request's method, URL and headers are printed correctly, but r.read() is always NULL. I can say it's not a problem in how the request is made, because content-length header is greater than zero on server side.

Documentation says r is a http.IncomingMessage object that implements the Readable Stream interface, so why it's not working?

lorenzo-s
  • 16,603
  • 15
  • 54
  • 86

3 Answers3

38

'readable' event is wrong, it incorrectly adds an extra null character to the end of the body string

Processing the stream with chunks using 'data' event:

http.createServer((r, s) => {
    console.log(r.method, r.url, r.headers);
    let body = '';
    r.on('data', (chunk) => {
        body += chunk;
    });
    r.on('end', () => {
        console.log(body);
        s.write('OK'); 
        s.end(); 
    });
}).listen(42646); 
BigMan73
  • 1,344
  • 15
  • 14
26

Ok, I think I've found the solution. The r stream (like everything else in node.js, stupid me...) should be read in an async event-driven way:

http.createServer(function(r, s) {
    console.log(r.method, r.url, r.headers);
    var body = "";
    r.on('readable', function() {
        body += r.read();
    });
    r.on('end', function() {
        console.log(body);
        s.write("OK"); 
        s.end(); 
    });
}).listen(42646);
lorenzo-s
  • 16,603
  • 15
  • 54
  • 86
  • 9
    It's not only an asynchronous approach but also event driven. The fact that you can operate effectively with functions and you are able to pass them as first-class functions and you can work with closures (anonymous functions), makes this method the most important programming method in javascript. Anyway I would prefer `r.on("data",function(chunk))` where you can directly work with the read data. In contrast to `readable`, `data` signals that something has been read from the stream, not that something can be read. And that is the preferred way for a io anyway. – ikrabbe Jun 23 '15 at 15:49
  • 2
    @BigMan73 is right - this will add a null to the end of the body string. Look at his answer instead. – Alexander Flenniken Oct 09 '20 at 15:56
1

From the official documentation https://nodejs.org/en/docs/guides/anatomy-of-an-http-transaction

let body = [];
request.on('data', (chunk) => {
  body.push(chunk);
}).on('end', () => {
  body = Buffer.concat(body).toString();
  // at this point, `body` has the entire request body stored in it as a string
});

If you want to use await you can convert it to a promise like that:

// how to call function:
const body = await getBody(request);

// function:
function getBody(request) {
  return new Promise((resolve) => {
    const bodyParts = [];
    let body;
    request.on('data', (chunk) => {
      bodyParts.push(chunk);
    }).on('end', () => {
      body = Buffer.concat(bodyParts).toString();
      resolve(body)
    });
  });
}
Welcor
  • 2,431
  • 21
  • 32