Get request body from node.js's http.IncomingMessage
Asked Answered
B

3

39

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?

Bombard answered 23/6, 2015 at 15:13 Comment(0)
B
29

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);
Bombard answered 23/6, 2015 at 15:30 Comment(2)
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.Cupellation
@Percival is right - this will add a null to the end of the body string. Look at his answer instead.Frayne
P
45

'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); 
Percival answered 3/1, 2019 at 15:38 Comment(0)
B
29

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);
Bombard answered 23/6, 2015 at 15:30 Comment(2)
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.Cupellation
@Percival is right - this will add a null to the end of the body string. Look at his answer instead.Frayne
H
8

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)
    });
  });
}
Herring answered 29/5, 2023 at 10:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.