Instead of sending a list of key/value pairs, I need to send a JSON string as the body of the POST request.
I make this POST request using jQuery's $.ajax function.
How do I set it correctly?
When I say JSON string, I mean something like: {action:'x',params:['a','b','c']} .
This is how I would use this JSON string in PHP on the server:
var_dump(json_decode(file_get_contents('php://input')));
Results in:
stdClass Object
action = x
params = Array
(
0 = a
1 = b
2 = c
)
Try:
$.ajax('url',{
'data': JSON.stringify(yourJSONObject), //{action:'x',params:['a','b','c']}
'type': 'POST',
'processData': false,
'contentType': 'application/json' //typically 'application/x-www-form-urlencoded', but the service you are calling may expect 'text/json'... check with the service to see what they expect as content-type in the HTTP header.
});
if you dont specify the key i think it will post as the body without the key like
$.ajax({
data:JSON.stringify({action:'x',params:['a','b','c']})
});
Related
I'm trying to remove jQuery from some code. I only use it for POST operations so I want to drop it and use fetch() instead. But I can't get fetch() to work using the same data. The php file is working OK, it is just not receiving the data
This sets up the test data for both test cases below:
var toPostObj = new(Object);
toPostObj.action = "update+file";
toPostObj.arrays = [["2020-12-28", "23:20:56", "Trying from ztest", "9.jpg"]];
This works using jQuery:
$.post('toMariaDB.php', { // url
data: toPostObj
}, function(data2, status, jqXHR) {
console.log ("data2",data2);
});
This does not work using fetch():
fetch("toMariaDB.php", {
method: "POST",
body: toPostObj, // using JSON.stringify(toPostObj) also doesn't work
headers: { "Content-type": "application/text; charset=UTF-8" }
})
.then(response => response.text())
.then(text => console.log(text))//;
.catch(error => {
console.error(error)
})
For debugging purposes, toMariaDB.php writes out a log file of the data it receives and any other messages from toMariaDB.
Running the jQuery code writes this to the log file:
toMariaDB: I've ARRIVED
in toMariaDB 1=>Array
(
[action] => update+file
[arrays] => Array
(
[0] => Array
(
[0] => 2020-12-28
[1] => 23:20:56
[2] => Trying from ztest
[3] => 9.jpg
)
)
)
which is what toMariaDB.php expects.
But the fetch() version writes this:
toMariaDB: I've ARRIVED
in toMariaDB 1=>
The result for fetch() is the same whether I use
body: toPostObj,
or
body: JSON.stringify(toPostObj),
I've used
headers: { "Content-type": "application/text; charset=UTF-8" }
since toMariaDB.php returns text and, as I understand it, headers describes what is returned
but just in case I had misunderstood, I tried
headers: { "Content-type": "application/json; charset=UTF-8" }
as well, but that didn't work either.
How can I format the body so that it arrives at toMariaDB.php in the same form as with jQuery? I.e.
toPostObj.action = "update+file";
toPostObj.arrays = [["2020-12-28", "23:20:56", "Trying from ztest", "9.jpg"]];
Thanks for any help.
EDIT
As suggested by #T.J.Crowder, (thanks for pointing me at that) here's what the Network tab shows as the Request Payload when running with jQuery:
data[action]: update+file
data[arrays][0][]: 2020-12-28
data[arrays][0][]: 23:20:56
data[arrays][0][]: Trying from ztest
data[arrays][0][]: 9.jpg
I don't understand why these don't show as data[arrays][0][0], etc., but it works.
(It's a 2D array because toMariaDB.php has to be able to process multiple arrays.)
With fetch(), the Network tab Request Payload shows:
[object Object]
From the documentation we can see that...
When data is an object, jQuery generates the data string from the object's key/value pairs unless the processData option is set to false. For example, { a: "bc", d: "e,f" } is converted to the string "a=bc&d=e%2Cf". If the value is an array, jQuery serializes multiple values with same key based on the value of the traditional setting (described below). For example, { a: [1,2] } becomes the string "a%5B%5D=1&a%5B%5D=2" with the default traditional: false setting.
(It doesn't say so, but it does it recursively.)
Your code is sending an object with a single top-level property called data whose value is your toPostObj, which in turn has properties with string and array values. It ends up sending a POST body that looks like this:
data%5Baction%5D=update%2Bfile&data%5Barrays%5D%5B0%5D%5B%5D=2020-12-28&data%5Barrays%5D%5B0%5D%5B%5D=23%3A20%3A56&data%5Barrays%5D%5B0%5D%5B%5D=Trying+from+ztest&data%5Barrays%5D%5B0%5D%5B%5D=9.jpg
...which is these parameters:
data[action]: update+file
data[arrays][0][]: 2020-12-28
data[arrays][0][]: 23:20:56
data[arrays][0][]: Trying from ztest
data[arrays][0][]: 9.jpg
You can replicate that with a URLSearchParams object like this:
var toPostObj = new URLSearchParams();
toPostObj.append("data[action]", "update+file");
toPostObj.append("data[arrays][0][]", "2020-12-28");
toPostObj.append("data[arrays][0][]", "23:20:56");
toPostObj.append("data[arrays][0][]", "Trying from ztest");
toPostObj.append("data[arrays][0][]", "9.jpg");
fetch("foo", {
method: "POST",
body: toPostObj
})
// ...
URLSearchParams will handle the URI-escaping etc. for you.
I think you'd have the best experience using this:
fetch("toMariaDB.php", {
method: "POST",
body: JSON.stringify(toPostObj),
headers: {
"Content-type": "application/json",
},
})
And doing json_decode on the PHP side.
If you want to exactly replicate what jQuery does, you'll have to look into the network tab as it can differ... but most likely it's using application/x-www-form-urlencoded as the content type. Best way to replicate that is to populate a FormData object which is still quite a hassle when you have arrays, especially nested arrays.
console.log("data ", data); // returns an object in JSON format {propertyName: propertyValue}
dataString = JSON.stringify(dataString); //correctly stringified json
let response = await fetch('updateRecevingEntry.php',
{
method:'POST',
headers: {'Content-Type':'application/json'},
body: dataString
}).then(response=>response.json());
however I get back an undefined index on the php side.
where the php is:
$matkey = $_POST['materialKey'];
returns
<b>Notice</b>: Undefined index: materialKey in <b>path/updateRecevingEntry.php</b> on line <b>3</b><br />
for all the data... none of it is getting caught.
so why is the _POST['propertyName'] not catching the stringData from the body?
I've tried a few variations, such as sending the data instead of the string data messing with the header, but I can't seem to figure out how to send the payload such that _POST['propertyName'] catches the data in the body.
I was using $.ajax from jquery before, and it was working: but I'm in the process of refactoring that out.
the Fetch api is new to me. where am I going wrong. I also don't want to parse a json object on the php side.
after reading one of the answers, I got it to work in one case,
but
let response = await fetch('updateRecevingEntry.php',
{
method:'POST',
headers: {'Content-Type':'application/json'},
body: sendData
}).then(response=>response.json());
and the php
$postData = json_decode(file_get_contents("php://input"), true);
var_dump($postData);
just returns a big fat NULL.
Edit two: turns out it just needs to actually be encoded via JSON.stringify(sendData). Since the. It works as expected.
The first thing I've noticed is that you're not using the right variable (you're using stringData instead of dataString):
dataString = JSON.stringify(dataString); //correctly stringified json
let response = await fetch('updateRecevingEntry.php', {
method:'POST',
headers: {'Content-Type':'application/json'},
body: dataString
}).then(response=>response.json());
Though you shouldn't need to stringify it as you're sending it with json headers.
Additionally, have you tried instead of $_POST, using php://input?
From PHP.net:
php://input is a read-only stream that allows you to read raw data from the request body. In the case of POST requests, it is preferable to use php://input instead of $HTTP_RAW_POST_DATA as it does not depend on special php.ini directives. Moreover, for those cases where $HTTP_RAW_POST_DATA is not populated by default, it is a potentially less memory intensive alternative to activating always_populate_raw_post_data. php://input is not available with enctype="multipart/form-data".
So you would use it like so:
$postData = json_decode(file_get_contents("php://input"), true);
$matkey = $postData['materialKey'];
This reads the body of the POST request as a JSON string then converts it to a PHP array.
I try to send jQuery AJAX request to my flask server :
$.ajax({
type: 'GET',
url: '/get',
dataType: "json",
contentType:"application/json",
data: JSON.stringify({ subject : "gpu",
filter : {
ids: [2, 3]
}
}),
success: function (data) {
console.debug(data);
}
});
And then I wait for a response from the server. Server part looks like this:
#api.route('/get', methods=['GET'])
def get():
response = None
try:
data = request.get_json()
response = do_some_magic(data)
except Exception as e:
respond = {'state': 'error', 'data': e.message}
finally:
return json.dumps(respond)
So, this combination doesn't work. request has only args field = ImmutableMultiDict([('{"subject":"gpu","filter":{"ids":[2,3]}}', u'')]) and json field = None.
But when in ajax request I set type: 'GET' and in flask get method methods=['GET'], server starts to handle requests correctly.
So, it would not be a real issue, but then I tried to send a GET request with postman utility. It's request:
GET /get HTTP/1.1
Host: localhost:5000
Content-Type: application/json
cache-control: no-cache
Postman-Token: 1d94d81c-7d93-4cf6-865a-b8e3e28278c1
{
"subject": "gpu",
"filter": {
"ids": [
2,
3
]
}
}------WebKitFormBoundary7MA4YWxkTrZu0gW--
And flask code worked with methods=['GET']. So the question is, what can cause such behaviour?
From jQuery documentation
data
Type: PlainObject or String or Array
Data to be sent to the server.
It is converted to a query string, if not already a string. It's appended to the url for GET-requests. See processData option to prevent this automatic processing. Object must be Key/Value pairs. If value is an Array, jQuery serializes multiple values with same key based on the value of the traditional setting (described below).
If processData is set to false, it means that the data string is still appended to the URL, just without some processing.
This may be due to the behaviour of XMLHttpRequest.send()
send() accepts an optional parameter which lets you specify the request's body; this is primarily used for requests such as PUT. If the request method is GET or HEAD, the body parameter is ignored and the request body is set to null.
I have a JSON Array that I am trying to post to SENDGRID using Ajax. Using Postman I am able to post with no issues however when I post the data in my .js file I keep getting an error (bad request = missing parameters).
Any help is appreciated.
Note: The values are in fact valid. I have removed the identifying information for safety.
CHROME PAYLOAD:
AJAX Call:
var mailUrl = "https://api.sendgrid.com/v3/mail/send";
var postdata = '{"personalizations": [{"to":[{"to email"}],"from": {"email":"from email"},"subject":"Hello, World!" , "content" : [{ "type":"text/plain" , "value":"TestMessage!" }]}]}'
$.ajax({
type: 'POST',
headers: {Authorization: "Bearer APIKEY"},
url: mailUrl,
contentType: "application/json",
data: JSON.stringify(postdata),
success: function (res) {
alert('ok');
},
error: function (res) {
alert('problems');
}
});
The problem seems to be with this part of json [{"to":[{"to email"}].You can use jsonlint to validate the json. Also JSON.stringify() method converts a JavaScript value to a JSON string.
But in your case postdata is already a string .
The string stored in the variable is a valid JSON. Calling JSON.stringify() on a JSON will escape all the special characters like " and that escaped string will not be deserialized to the object you intended.
While a string is still a valid JSON according to some specifications, The specifications for application/json stated in RFC4627
An object structure is represented as a pair of curly brackets
surrounding zero or more name/value pairs (or members).
make the returned string invalid for post.
Sending the string itself without serializing it again will likely work.
My problem is very common. I must be doing some silly mistake somewhere but I am not able to figure it out.
I am send my form data in serialized form but it is not coming to PHP at all.
Angular JS code:
saveForm: function() {
var str = $('#feedbackForm').serializeArray();
alert(JSON.stringify(str)); // here I am getting my data properly
return $http({
method :'POST',
url:'http://localhost/api?module=form&app_id=APP001&action=save&formid=2&user_id=3',
data: str
});
}
PHP
$log->info($_REQUEST); // I am getting all GET parameters correctly
tried this also
$log->info($_POST);
it is not printing my data. why?
By default, the $http will send the data as application/json, which won't be recognized by the $_POST in PHP.
You have to choose either sending the data as form data like this:
return $http({
method: 'POST',
url: 'http://localhost/api?module=form&app_id=APP001&action=save&formid=2&user_id=3',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: str
});
Or don't use the $_POST but read and parse input directly in PHP like this:
$rawInput = file_get_contents('php://input');
$data = json_decode($rawInput);
Hope this helps.