1

I am trying to send JSON data using XMLHttpRequest in WordPress:

document.getElementById("user_vote").addEventListener("click", function(event) {
    event.preventDefault();

    //action to handle in WP add_action("wp_ajax_my_user_vote", "my_user_vote");
    let action = "my_user_vote";
    let url = myAjax.ajaxurl;

    let data = {
        action: action,
        post_id : this.dataset.post_id,
        nonce: this.dataset.nonce
    };

    let json = JSON.stringify(data);

    let xhr = new XMLHttpRequest();

    xhr.open("POST", url, true);
    xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8');

    xhr.onreadystatechange = function() {
        if (this.readyState != 4) return;

        document.getElementById('response').innerHTML = this.responseText;
    }

    xhr.send(json);
});

When I am setting headers to JSON xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8'); I am getting empty $_REQUEST variable. It seems that WP do not understanding this headers or I am doing something wrong.

I saw this post. When I am checking $input = file_get_contents('php://input'); in admin-ajax.php it returning string of my data: {"action":"my_user_vote","post_id":"1","nonce":"2b2eaea6d3"} but $_REQUEST variable is empty.

All working good with this headers xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); but I want to send JSON.

Why using jQuery.ajax I can simple send JSON data to admin-ajax.php and cant do it with javascript?

Add 1

Here are the right way to send AJAX in WP:

jQuery.ajax({
    type : "post",
    dataType : "json",
    url : myAjax.ajaxurl,
    data : {action: "my_user_vote", post_id : post_id, nonce: nonce},
    success: function(response) {
        //do stuff
    }
})

You mean that in this case jQuery will not set headers to application/json but will set them to application/x-www-form-urlencoded and all this request will be produced to simple XMLHttpRequest with standard headers and xhr.send(data) converted to string, something like this "action=my_user_vote&post_id=1&nonce=2b2eaea6d3"?

And I do not need to set headers to application/json and use simple POST request with standard headers?

Alexander
  • 111
  • 2
  • 12
  • If you set the jQuery ajax `data` property to an object like yours, jQuery will convert that to ordinary form arguments. – Pointy Mar 13 '19 at 12:59
  • @Pointy the OP doesn't want to use ajax. Also, remove ' charset=utf-8' from the content-type header, it's not supposed to be included in that. – garry man Mar 13 '19 at 13:01
  • @garryman — The OP is asking why jQuery and XHR appear to have different behaviours. The comment is entirely relevant. – Quentin Mar 13 '19 at 13:01
  • @garryman — There is nothing wrong with the inclusion of a `charset` paramater in a `Content-Type` header. – Quentin Mar 13 '19 at 13:02

1 Answers1

4

PHP will not automatically decode JSON and put it in $_POST or $_REQUEST.

As you have already discovered, you need to read the raw HTTP request body from STDIN and parse it explicitly to read a JSON formatted request in PHP.


Your problem appears to be a misunderstanding of what jQuery does. Probably when you are using jQuery you are doing something like this:

var data = { some: "object" };
jQuery.ajax({
    url: "foo",
    data: data,
    method: "POST"
});

When you do that, jQuery will URL Encode the data object. It will not JSON encode it.

Remember, while JSON syntax is inspired by JavaScript literal syntax, the results of having an object literal in JavaScript source code will give you an object and not a string of JSON.

If you were using jQuery to make a JSON formatted request you would need something like this:

jQuery.ajax({
    url: "foo",
    data: JSON.stringify(data),
    contentType: "application/json; charset=utf-8",
    method: "POST"
});

To encode your object as form data with XHR, you would want something along the lines of:

var data = { some: "object" };
var array = [];
Object.keys(data).forEach(element => 
    array.push( 
        encodeURIComponent(element) + "=" + encodeURIComponent(data[element])
    )
);
var body = array.join("&");
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send(body);
Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335