1

I have simple app.js for Node.js under localhost:3000

app.js:

let http = require('http');

http.createServer((req, res) => {
    res.writeHead(200);
    let response;
    if(~req.url.indexOf('post')) {
        response = req.body.content;
    } else {
        response = '<script src="http://localhost/fetch.js"></script>';
    }
    res.end(response);
}).listen(3000);

The file fetch.js is placed on my another local server and is successfully enqueued to the page

fetch.js:

read('http://localhost:3000/?post').then((response) => {
    console.log(response);
});
async function read(url) {    
    const response = await fetch(url, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json;charset=utf-8',
        },
        body: JSON.stringify({
            content: 'Text'
        })
    });
    return response.text();
}

So I render HTML with fetch.js which then send POST request to the same page, but with a query ?post

However, when I run node app.js I get the error

Can not read property 'content' of undefined

So I don't get req.body

Why and how to resolve?

stckvrw
  • 1,689
  • 18
  • 42
  • if ~req.url…? what do you expect this to do? – Psi Aug 19 '22 at 17:42
  • 1
    If `req.url` contains `'post'`. This condition is working, so is not a problem – stckvrw Aug 19 '22 at 17:49
  • 1
    Is really req.body empty? Isn't it string, that has to be JSON.parsed? Pretty nice post about your code: https://stackoverflow.com/questions/12006417/node-js-server-that-accepts-post-requests – Fide Aug 19 '22 at 17:49
  • this would be `if (req.url…)` as opposed to `if (~req.url…)`. ~ is the arithmetic `NOT` operator, i don’t see the use here. it may, however, negate your intended logic – Psi Aug 19 '22 at 17:52
  • @Psi https://wsvincent.com/javascript-tilde/ – GrafiCode Aug 19 '22 at 17:55
  • 2
    @stckvrw could you please `console.log(req.body)`? – GrafiCode Aug 19 '22 at 17:56
  • 2
    @Psi `~` is commonly used with `indexOf` as it returns `-1` when no elements are found. `~ -1` becomes `0` – Konrad Aug 19 '22 at 17:59
  • @GrafiCode if I add `console.log(req.body)` I get `undefined` – stckvrw Aug 19 '22 at 19:28
  • i think you are missing parser for your http server, there is no body because you actually didn't parse the body. check this https://frontendguruji.com/blog/how-to-parse-post-request-in-node-js-without-expressjs-body-parser/ – Ali Aug 19 '22 at 20:09
  • 1
    @Ali with your code I receive `parseJson is not a function` – stckvrw Aug 22 '22 at 12:37
  • @stckvrw sorry, i updated the post. you may use it now – Ali Aug 23 '22 at 05:57

2 Answers2

2

i think you are missing parser for your http server, there is no body because you actually didn't parse the body.

assemble the chunks like below then parse it as the header sais.

this is my work for myself

private parseBody(req: IncomingMessage) {
    return new Promise((resolve, reject) => {
      const chunks: any[] = []

      req.on("data", (chunk) => {
        chunks.push(chunk)
      })

      req.on("end", () => {
        const data = Buffer.concat(chunks)

        switch (req.headers["content-type"]) {
          case "application/json":
            resolve(this.parseJson(data.toString()))
            break

          case "application/x-www-form-urlencoded":
            resolve(this.parseUrlEncoded(data.toString()))
            break

          default:
            resolve({})
        }
      })
    })

http server is very abstract and doesn't support anything basicly, i suggest using express or fastify.

working example: https://frontendguruji.com/blog/how-to-parse-post-request-in-node-js-without-expressjs-body-parser/

update

this is the class im using

http.resolver.ts

 private parseJson(data: string) {
    return JSON.parse(data)
  }

  private parseUrlEncoded(data: string) {
    const parsedData = new URLSearchParams(data)

    const dataObj: any = {}

    for (var pair of parsedData.entries()) {
      dataObj[pair[0]] = pair[1]
    }

    return dataObj
  }

  private parseBody(req: IncomingMessage) {
    return new Promise((resolve, reject) => {
      const chunks: any[] = []

      req.on("data", (chunk) => {
        chunks.push(chunk)
      })

      req.on("end", () => {
        const data = Buffer.concat(chunks)

        switch (req.headers["content-type"]) {
          case "application/json":
            resolve(this.parseJson(data.toString()))
            break

          case "application/x-www-form-urlencoded":
            resolve(this.parseUrlEncoded(data.toString()))
            break

          default:
            resolve(parse(req.url ?? "/", true).query)
        }
      })
    })
  }

you may use await behind the parseBody function after

Ali
  • 615
  • 10
  • 16
0

Ok, thanks to @Fide. The link he posted has the answer:

let http = require('http');

http.createServer((req, res) => {
    if(~req.url.indexOf('post')) {
        let body;
        req.on('data', function(data) {
            body = data;
        })
        req.on('end', function() {
            res.writeHead(200);
            res.end(body);
        })
    } else {
        res.writeHead(200);
        res.end('<script src="http://localhost/fetch.js"></script>');
    }
}).listen(3000);
stckvrw
  • 1,689
  • 18
  • 42