7

I am trying to send JSON data from a form using the XMLHttpRequest object. I can send the data using the following function. There are no errors displayed in FireBug and the JSON-data in the request is displayed well formed by FireBug.

However, I send the data to echo.php, what simply returns the content:

<?php
print_r($_POST);
print_r($_GET);
foreach (getallheaders() as $name => $value) {
    echo "$name: $value\n";
}
echo file_get_contents('php://input');
?>

The POST-array is always empty, but I can see the JSON string returned by file_get_contents. How does that happen? What am I doing wrong?

output of echo.php

Array
(
)
Array
(
)
Host: localhost
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:10.0.2) Gecko/20100101 Firefox/10.0.2
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: eo,de-de;q=0.8,de;q=0.6,en-us;q=0.4,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Type: application/json; charset=utf-8
Referer: http://localhost/form.html
Content-Length: 88
Cookie: {{..to much data..}}
Pragma: no-cache
Cache-Control: no-cache
{"type":"my_type","comment":"commented"}

the sending function:

function submit(){
    var data={};
    data.type=document.form.type.value;
    data.comment=document.form.comment.value;

    //get right XMLHttpRequest object for current browsrer
    var x=ajaxFunction();

    var string = JSON.stringify(data);

    x.open('POST','echo.php',true);
    x.setRequestHeader('Content-type','application/json; charset=utf-8');
    x.setRequestHeader("Content-length", string.length);
    x.setRequestHeader("Connection", "close");

    x.onreadystatechange = function(){
        if (x.readyState != 4) return;
        if (x.status != 200 && x.status != 304) {
            alert('HTTP error ' + req.status);
            return;
        }

        data.resp = JSON.parse(x.responseText);
        if(data.resp.status=='success'){
            alert('That worked!');
        }else{
            alert('That didn\'t work!');
        }
    }
    x.send(string);

    return false; //prevent native form submit
}
Simon
  • 4,395
  • 8
  • 33
  • 50

2 Answers2

8

PHP does not process JSON requests automatically like it does with form-encoded or multipart requests. If you want to use JSON to send requests to PHP, you're basically doing it correctly with file_get_contents(). If you want to merge those variables into your global $_POST object you can, though I would not recommend doing this as it might be confusing to other developers.

// it's safe to overwrite the $_POST if the content-type is application/json
// because the $_POST var will be empty
$headers = getallheaders();
if ($headers["Content-Type"] == "application/json")
    $_POST = json_decode(file_get_contents("php://input"), true) ?: [];

Quick note: you should not be sending a charset with your Content-Type for application/json. This should only be sent with text/* Content-Types.

rich remer
  • 3,407
  • 2
  • 34
  • 47
  • This snippet does not work out of the box. `getallheaders()` function doesn't exist in PHP-FPM. Moreover, there is even no need for that, because you can safely get `$_SERVER["HTTP_CONTENT_TYPE"]` instead. – The Godfather Mar 07 '20 at 21:16
  • `getallheaders` is available in PHP-FPM as of v7.3. – rich remer Jun 14 '21 at 22:41
7

You forgot to name your variables in the send function. The good way to use it is

x.send('name1='+string+'&name2=value2'); 

Given that, I think you will have to change the content-length header. I don't think it is usefull to send it.

One another thing you can do is try with GET method. You can also try to change your content-type header by that one :

xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
shybovycha
  • 11,556
  • 6
  • 52
  • 82
artragis
  • 3,677
  • 1
  • 18
  • 30
  • already tried this but it does not change the behavior. I receive `data={"type":"my_type","comment":"commented"}` in PHP, but `$_POST` is empty. Do I have to escape the `string`? How? – Simon Mar 01 '12 at 12:27
  • 1
    One thing you can do is replace your content-type header with that one : x.setRequestHeader("Content-type", "application/x-www-form-urlencoded") – artragis Mar 01 '12 at 12:44
  • Thanks! That worked! Seems very unhandy to me. But if it is the only way I will do it like this. – Simon Mar 01 '12 at 12:53
  • Oh, and I escaped the JSON string using the escape method from: http://stackoverflow.com/a/9204218/487846 – Simon Mar 01 '12 at 12:56
  • 1
    If you do `x.setRequestHeader("Content-type", "application/x-www-form-urlencoded")` you are sending form data, not JSON data. The answer below by rich remer is correct. – Stijn de Witt Jul 24 '15 at 11:39