0

So I'm trying to POST to my server (php) from Javascript and am trying to not use JQuery.

This code works and posts the necessary data to the database

var msg = {};
msg['name'] = 'joe';
msg['message'] = 'why no work';

$.post(phpURL, msg, function(data) {});

but this one does not

var xhr = new XMLHttpRequest();
xhr.open("POST", phpURL, true);
xhr.send(msg);

I even looked at my php logs, looked at the headers and the only difference of the JQuery one from the XHR one I could see was the content-type header "application/x-www-form-urlencoded; charset=UTF-8" and this header "x-requested-with" "XMLHttpRequest".

So I tried all combinations of the following headers.

var xhr = new XMLHttpRequest();
xhr.open("POST", phpURL, true);
//xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
//xhr.setRequestHeader('x-requested-with', 'XMLHttpRequest');
xhr.send(msg);

to no effect.

It is worth mentioning if I try to add JSON.stringify(msg) anywhere, it does not work, neither in JQuery or XHR. But I would like to get this working first, and explain this bizarre difference.

I am inclined to believe this is a Javascript issue since the JQuery post works and in addition, a GET request of the server and the same table I'm trying to post to does work.

Matthias
  • 3,160
  • 2
  • 24
  • 38
  • 3
    You've format the data to send to `application/x-www-form-urlencoded` format when sending a native XHR, jQuery does that automatically for you. – Teemu Mar 25 '21 at 18:31
  • 1
    That thread above discusses data format – devlin carnate Mar 25 '21 at 18:33
  • Yes thank you both, you are correct. If I change the msg to be 'name=bla&message=ble' it does work. I want to send as JSON though because I need to send utf8 but I guess that will have to be another question. – Matthias Mar 25 '21 at 18:37
  • JSON is not an enctype. You need to set `Content-Type', 'text/plain; charset=UTF-8'` header, if you want to send JSON. The data reading on the server differs from the traditional handling in this case. – Teemu Mar 25 '21 at 18:44
  • thank you for the help, it still doesn't work. The POST object is empty in my PHP code, I have no idea why – Matthias Mar 25 '21 at 18:53
  • 1
    Like said earlier, "_data reading on the server differs from the traditional_". Read the value using `file_get_contents('php://input');`. It returns you the JSON as a string (providing you send a stringified object). – Teemu Mar 25 '21 at 19:01
  • thank you very much, I didn't look at this answer but slept on this and after 6 hours of frustration yesterday this came to me this morning like a light in the darkness. I have actually done this before some years ago but just forgot ahaha – Matthias Mar 26 '21 at 09:46

2 Answers2

4

Do not confuse JavaScript objects with JSON.

If you pass an object to jQuery's data parameter then it will encode it as application/x-www-form-urlencoded data (not JSON!).

If you POST application/x-www-form-urlencoded data to PHP then it will parse it and populate the $_POST superglobal with it.

If you pass an object to the send() method of an XMLHttpRequest object then it will not encode it for you. It will invoke .toString() on it implicit and send nothing very useful at all.

To achieve the same effect as jQuery will do then you need to encode the data yourself. Don't forget to also set the Content-Type header!

const encoded = new URLSearchParams(Object.entries(msg)).toString();
const xhr = new XMLHttpRequest();
xhr.open("POST", phpURL, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(encoded);

If you want to send JSON then you also have to encode it, but that is done simply with JSON.stringify() although you also need to set the Content-Type header (to application/json this time).

const encoded = JSON.stringify(msg);
const xhr = new XMLHttpRequest();
xhr.open("POST", phpURL, true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(encoded);

However, PHP will not parse JSON automatically so $_POST will remain empty, so you need to parse it manually.

<?php 
    $json = file_get_contents('php://input');
    $msg = json_decode($json);
Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
1

jQuery does a lot of work under the hood when sending an AJAX request. It converts a live object in application/x-www-form-urlencoded format and fixes the headers, and does other tasks depending on the arguments.

When you're sending a native XMLHttpRequest, you've to handle all this by yourself. In the comments you said, you actually want to send JSON. JSON is not a standard form content type PHP would recognize, hence you've to send it in application/json format. Like this:

var msg = {
    name: 'joe',
    message: 'why no work'
  },
  xhr = new XMLHttpRequest();

xhr.open('POST', phpURL, true);
xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
// Add response handling here, if needed
xhr.send(JSON.stringify(msg));

However, content type of application/json is not considered as form data in PHP, and $_POST will be empty. You can read the data from the received raw data, which is saved in a specific file. Like this:

$data = file_get_contents('php://input');
// Results a string: '{"name":"joe","message":"why no work"}'

Now you can convert the JSON to associative array with json_decode($data).

Teemu
  • 22,918
  • 7
  • 53
  • 106
  • JSON is not plain text. The content-type for JSON is `application/json` – Quentin Mar 25 '21 at 19:22
  • @Quentin ??? Technically JSON is plain text, I'll check `application/json`, just a second. – Teemu Mar 25 '21 at 19:23
  • JSON is text, but it is formatted text, not plain text. – Quentin Mar 25 '21 at 19:23
  • @Quentin I think PHP doesn't make a difference, but maybe some other languages will do? – Teemu Mar 25 '21 at 19:24
  • 1
    If you don't write code to pay attention to what Content-Type you are sending, then it is pretty much irrelevant… but a bad practise that could trip you up in future (e.g. if you later switch your backend and use a library to parse the incoming data (which pays attention to the content-type) instead of just assuming you'll be sent JSON. – Quentin Mar 25 '21 at 19:26
  • 1
    @Quentin You're absolutely right. It looks like I've some job to do at the weekend, I've to fix a ton of requests sending JSON data on my sites. – Teemu Mar 25 '21 at 19:43