I'm having trouble processing an AJAX request on my Wordpress site. A plugin that I'm writing needs to serve a shortcode with a script that should dispatch an AJAX request back to the plugin, which should send back some JSON data.
Shortcode [payment_form] being served:
public function initPaymentFormShort()
{
ob_start();
wp_register_script('Stripe', 'https://js.stripe.com/v3/', null, null, true);
wp_enqueue_script('Stripe');
wp_enqueue_script('payment.js', get_template_directory_uri() . '/js/payment.js');
wp_localize_script( 'payment.js', 'ajax_post_params', array( 'ajax_post_url' => admin_url( 'admin-ajax.php' ) ) );
include __DIR__ . '/../templates/payment_form_short.php';
return ob_get_clean();
}
Request handler registered to process the request:
add_action('wp_ajax_get_public_key', array($this, 'handleGetPublicKey'));
add_action('wp_ajax_nopriv_get_public_key', array($this, 'handleGetPublicKey'));
Handlers are registered within the plugins_loaded action
Part of the payment.js script that dispatches the request:
function getPublicKey() {
return fetch(ajax_post_params.ajax_post_url, {
method: 'post',
body: JSON.stringify({action : 'get_public_key'})
})
.then(function(response) {
return response.json();
})
.then(function(response) {
stripeElements(response.publicKey);
});
}
The request gets sent to: http://mysite.local/wp-admin/admin-ajax.php (served with Flywheel Local if that matters)
The response is a 400 Bad Request with a 0 in the body. The handler doesn't get executed at all.
Please help. Thanks
Wordpress's ajax API does not use JSON encoded requests. You need to do form-encoded. Something like this should work:
function getPublicKey() {
return fetch(ajax_post_params.ajax_post_url, {
method: 'post',
body: 'action=get_public_key',
headers: { 'Content-type': 'application/x-www-form-urlencoded' }
})
.then(function(response) {
return response.json();
})
.then(function(response) {
stripeElements(response.publicKey);
});
}
Note that the body is urlencoded. So if you had multiple parameters it would look something like this:
action=get_public_key&foo=bar&baz=bin
All modern browsers also support the URLSearchParams API so you can also generate these query strings like this:
let params = new URLSearchParams({
action: 'get_public_key',
foo: 'bar',
baz: 'bin'
});
let body = params.toString();
That would be the preferred way of generating form data query strings for complex data sets.
Related
This was my jQuery code before. Now I want to change it to fetch.
function fetch(){
jQuery.ajax({
url: '<?php echo admin_url('admin-ajax.php'); ?>',
type: 'post',
data: { action: 'data_fetch', keyword: jQuery('#keyword').val(), pcat: jQuery('#cat').val() },
success: function(data) {
jQuery('#datafetch').html( data );
}
});
}
I have changed the code to this now but I am getting bad request 400 status code
document.querySelector('#keyword').addEventListener('keyup',()=>{
let data = {
action: 'data_fetch',
keyword: document.querySelector("#keyword").value
};
let url = "<?php echo admin_url('admin-ajax.php'); ?>";
fetch(url, {
method: 'POST', // or 'PUT'
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then((response) => response.text())
.then((data) => {
document.querySelector("#datafetch").innerHTML = data;
})
.catch((error) => {
console.error('Error:', error);
});
})
Am I missing something? It is from WordPress if this helps somehow.
You forgot the pcat property in the data object
jQuery, by default, sends form encoded data, not JSON encoded data. Use a URLSearchParams object instead of a string of JSON and omit the content-type header (the browser will add the correct one for you).
In your jQuery code you defined data as
data: { action: 'data_fetch', keyword: jQuery('#keyword').val(), pcat: jQuery('#cat').val() },
and in fetch you defined it as
let data = {
action: 'data_fetch',
keyword: document.querySelector("#keyword").value
};
so, you are not passing some value which was previously passed. No wonder that your server errors out. Let's change your code to
let data = {
action: 'data_fetch',
keyword: document.querySelector("#keyword").value,
pcat: document.getElementById("cat").value
};
and try this out. If it still works, then you will need to find out what differs in the request and make sure that the request you are sending via fetch is equivalent to the request you formally sent via jQuery, then it should work well, assuming that the client-side worked previously with jQuery and the server-side properly handled the requests.
I'm trying to send form data from a NativeScript app to a TYPO3-Webservice.
This is the JavaScript I'm using:
httpModule.request({
url: "https://my.domain.tld/webservice?action=login",
method: "POST",
headers: { "Content-Type": "application/json" },
content: JSON.stringify({
username:username,
password:password
})
}).then((response) => {
console.log("got response");
console.log(response.content);
//result = response.content.toJSON();
callback(response.content.toJSON());
}, (e) => {
console.log("error");
console.log(e);
});
But I can't read this data in the controller. Even with this:
$rest_json = file_get_contents("php://input");
$postvars = json_decode($rest_json, true);
$postvars is empty. $_POST is empty, too (which is - according to some docs - because the data is sent as JSON and thus the $_POST-Array isn't populated.
Whatever I do, whatever I try, I can't get those variables into my controller.
I tried it with fetch as well as with formData instead of JSON.stringify, same result.
I might have to add, that when I add the PHP-part in the index.php of TYPO3, $postvars is being populated. So I guess something goes missing, until the controller is called.
Any ideas?
the nativescript part seems ok, your problem must be corrected in the server side.
i use similare call and its works
// send POST request
httpModule.request({
method: "POST",
url: appSettings.getString("SERVER") + '/product/list',
content: JSON.stringify(data),
headers: {"Content-Type": "application/json"},
timeout: 5000,
}).then(response => { // handle replay
const responseAsJson = response.content.toJSON();
console.log('dispatchAsync\n\tresponse:', responseAsJson);
}, reason => {
console.error(`[ERROR] httpModule, msg: ${reason.message}`);
});
This question already has answers here:
Receive JSON POST with PHP
(12 answers)
Closed 2 years ago.
I have this fetch method
loginbutton.onclick = (e)=> {
e.preventDefault();
var creds = {
login: login.value,
pass: pass.value
}
fetch('functions/asklogin.php', {
method: "POST",
header: {"Content-type": "application/json; charset=UTF-8"},
body: JSON.stringify(creds)
})
.then(resp => resp.text())
.then(data => console.log(data));
}
When I click, my xhr body is well fed:
But, if I try to echo these parameters from my asklogin.php
echo "no".$_POST['pass'].":".$_POST['login'];
All I get is no:
Thank you for your help
$_POST in PHP will recognize the only the form data (application/x-www-form-urlencoded or multipart/form-data) with a specified content type header.
For any other content type, you'll need to use php://input to read and parse the data.
For your case, changing the data you send through fetch should solve the issue.
...
fetch('functions/asklogin.php', {
method: "POST",
headers: {"Content-type": "application/x-www-form-urlencoded; charset=UTF-8"},
body: Object.entries(creds).map(([k,v])=>{return k+'='+v}).join('&') // in jQuery simply use $.param(creds) instead
})
...
An alternative solution will be changing how the PHP side reads data
...
if( is_empty($_POST) ){ $_POST = json_decode(file_get_contents('php://input'), true) }
...
So I have this code:
axios({
method: 'post',
url,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: {
json,
type,
}
})
Originally I had the normal axios.post but I changed to this because I thought it might have been a header problem. However I am still detecting nothing in my $_REQUEST nor $_POST. However, it is receiving data in file_get_contents("php://input").
Any idea what is wrong?
Edit
Okay I think I know what's wrong. It's posting it as a json object so it can only be read in the php://input. How do I change it to a normal string in axios?
From the documentation (I haven't preserved links in the quoted material):
Using application/x-www-form-urlencoded format
By default, axios serializes JavaScript objects to JSON.
PHP doesn't support JSON as a data format for populating $_POST.
It only supports the machine-processable formats natively supported by HTML forms:
application/x-www-form-urlencoded
multipart/form-data
To send data in the application/x-www-form-urlencoded format instead, you can use
one of the following options.
Browser
In a browser, you can use the URLSearchParams API as follows:
var params = new URLSearchParams();
params.append('param1', 'value1');
params.append('param2', 'value2');
axios.post('/foo', params);
Note that URLSearchParams is not supported by all browsers, but there
is a polyfill available (make sure to polyfill the global
environment).
Alternatively, you can encode data using the qs library:
var qs = require('qs');
axios.post('/foo', qs.stringify({ 'bar': 123 }));
Or you could customise your PHP so it can handle JSON as per this answer on another question.
var params = {
data1: 'string',
}
axios.post(url, params).then(function(response) {
//code here
});
or
axios.post(url, {data1: 'string' }).then(function(response) {
//code here
});
api
$_POST = json_decode(file_get_contents("php://input"),true);
echo $_POST['data1'];
To make things easier and universal if you ever decided to switch between AJAX libraries or server languages. With axios use the native JS FormData.
If you have your data in an object, you can convert it to FormData like this:
var myDataObj = {id:1, name:"blah blah"}
var formData = new FormData();
for (var key in myDataObj) {
formData.append(key, myDataObj[key])
}
Then you send the data:
axios.post('/sub/process.php', formData, {
params: { action: "update-user" },
headers: { 'Content-Type': 'multipart/form-data' },
baseURL: 'http://localhost',
}).then(data =>
console.log(data)
).catch(err => {
console.log(err)
return null
})
Notice, you can also send some info using params in axios that you can retrieve using $_GET. Also notice that I am using the baseURL in case you have different servers for the web page and your API endpoint.
You need to understand also that before axios send the real request, it performs a preflight request. A preflight request, is a mechanism in CORS by the browser to check if the resource destination is willing to accept the real request or not. Afterall, why would a request be sent when the target host is not willing to receive it anyway?
You have to make sure that your server has the right headers for your axios request, otherwise the preflight request will detect the incompatibility and stop your request:
//this is if you are using different different origins/servers in your localhost, * to be update with the right address when it comes to production
header('Access-Control-Allow-Origin: *');
//this is if you are specifying content-type in your axios request
header("Access-Control-Allow-Headers: Content-Type");
Now, you will able to access your sent data in the $_POST variable:
echo "<pre>";
print_r($_POST);
echo "</pre>";
Additionally, axios allows you to send data in different formats. you can send a json for example like this:
axios.post('/sub/process.php', { id: "1", name:"blablah" }, {
params: { action: "update-item" },
headers: { 'Content-Type': 'application/json' },
baseURL: 'http://localhost',
}).then(data =>
console.log(data)
).catch(err => {
console.log(err)
return null
})
In the PHP side, this can be accessed as follows:
$data = json_decode(file_get_contents("php://input"),true);
echo "<pre>";
print_r($data);
echo "</pre>";
Using PHP std object
Using PHP std object structure to get the variables of the post.
On the client:
axios.post(url, {id: 1 , Name:'My Name' }).then(function(response) {
console.log(response.data);
});
On the server
$obj = json_decode(file_get_contents('php://input'));
$id = $obj->id;
$Name = $obj->Name;
//test by returning the same values
$retObj=(object)["id"=>$id,"Name"=>$Name]
echo json_encode($retObj);
Both jQuery and Axios using same PHP file
if you have a file receiving post both from axios and jquery you may use:
if($_SERVER['REQUEST_METHOD']==='POST' && empty($_POST)) {
$_POST = json_decode(file_get_contents('php://input'),true);
}
to convert the Axios json-serialized posts to the $_POST array
This code works on browser/node both today.
I think this is more practical.
I tested this code on node.js and passed the data variable to PHP8 using $_POST['param1'] and it worked perfectly.
function axqs(d){
let p = new URLSearchParams();
Object.keys(d).forEach(function(key){
p.append(key, this[key]);
}, d);
return p
}
let data = {
'param1': 'value1',
'param2': 'value2',
}
let p = axqs(data)
axios.post('/foo', p)
Just wanted to share my insights, I was facing a similar problem and solved it by the following code set
JS
const instructions_str = {
login: {
"type": "devTool",
"method": "devTool_login",
"data": {
"username": "test",
"password": "Test#the9"
}
},
tables: {
"type": "devTool",
"method": "devTool_get_all_tables",
"data": ""
}
};
const body = {
firstName: 'Fred',
lastName: 'Flintstone',
name: "John",
time: "2pm",
instructions : JSON.stringify(instructions_str)
};
function decodeData(data) {
const serializedData = []
for (const k in data) {
if (data[k]) {
serializedData.push(`${k}=${encodeURIComponent(data[k])}`)
}
}
return serializedData.join('&')
};
const body2 = decodeData(body);
axios.post('URL', body2)
.then(response => {
console.log("contextApi got it", response);
}).catch(error => {
console.log("contextApi error.response", error.response);
});
PHP
// set content return type
header('Content-Type: application/json');
// Setting up some server access controls to allow people to get information
header("Access-Control-Allow-Origin: *");
header('Access-Control-Allow-Methods: POST, GET');
// This way I can check and see what I sent
$postVars_array = $_POST ?? parse_str(file_get_contents("php://input"),$postVars_array) ?? [];
echo json_encode($postVars_array);
I also found this github page very helpful https://github.com/axios/axios/issues/1195
Right so I have a angular function that is sending data to a php file using the http method.The php code I want to process the data and echo it back onto the page to confirm that the php file has processed it. I'm currently getting undefined alerted back to me, surely I should be getting the vaule of email back to me?. Thanks all
I'm following this tutorial https://codeforgeek.com/2014/07/angular-post-request-php/
var request = $http({
method: "post",
url: "functions.php",
data: {
email: $scope.email,
pass: $scope.password
},
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
//.then occurs once post request has happened
//success callback
request.success(function (response) {
alert(response.data);
},
//error callback
function errorCallback(response) {
// $scope.message = "Sorry, something went wrong";
alert('error');
});
My php code...
//receive data from AJAX request and decode
$postdata = file_get_contents("php://input");
$request = json_decode($postdata);
#$email = $request->email;
#$pass = $request->pass;
echo $email; //this will go back under "data" of angular call.
From the documentation:
The $http legacy promise methods success and error have been deprecated. Use the standard then method instead. If $httpProvider.useLegacyPromiseExtensions is set to false then these methods will throw $http/legacy error.
Your code should look like this:
request.then(
function( response ) {
var data = response.data;
console.log( data );
},
function( response ) {
alert('error');
}
);
Now, you need to encode the response from the server in a JSON format, so replace echo $email; with:
echo json_encode( array(
'email' => $email
) );
And you can access the email property from the angularjs $http.success promise callback function (it's the first function inside the then closure) by response.data.email