not getting POST data with application/json - php

I'm using Backbone, PHP(5.6) and Apache(2.4).
Problem: When posting data using Backbone's model.save() the $_POST array is empty on the server.
I know about different ways to fix this.
Client way: setting Backbone.emulateJSON to true or changing the Backbone source.
Server way: reading raw body instead of $_POST.
These work but they feel like hacks and in fact they end up being a bit inconvenient. Also, Backbone's doc says:
If you're working with a legacy web server that can't handle requests encoded as application/json, setting Backbone.emulateJSON = true; will [fix it].
So it sounds like a server problem. How do I make my server non-legacy?

Thanks to the comments I realized this is correct behavior.
$_POST is for form data, while json data (application/json) should be gotten from the raw input.

Related

How can I parse multipart/form-data from an HTTP PUT request into an associative array in PHP?

I'm trying to write a REST API in PHP from the ground up for my website, partly as a learning exercise, partly to develop a codebase that I can reference later in case I forget how something works.
To my dismay, I've discovered that PHP has no $_PUT superglobal.
Remembering that Laravel makes GET/PUT/POST/DELETE distinctions, I figured Laravel must have code to handle HTTP PUT requests correcly, but no, in fact, it depends on a hidden form field with the value "_PUT" to specify the action to take.
Without the need to process files, is there any way to take multipart/form-data and parse it into an associative array in a similar fashion to $_POST, such that it is foreach iterable?
Here is what I tried and it simply doesn't work. I'm not understanding what the extra data is that is sent, must be related to the PHP session?
parse_str(file_get_contents("php://input"), $_PUT);
foreach ($_PUT as $key => $value)
{
unset($_PUT[$key]);
$_PUT[str_replace('amp;', '', $key)] = $value;
}
$_REQUEST = array_merge($_REQUEST, $_PUT);
foreach($_PUT as $key=>$value){
$ani->state[$key]['value'] = $value;
}
What I end up getting out of this looks like this: (I am pretty sure I can beat this into what I want, but I don't think what I come up with is going to be robust enough to trust not to break all the time.)
So as not to anger anyone, the code I'm using came directly from here: https://joshtronic.com/2014/06/01/how-to-process-put-requests-with-php/
I tried to spin my own, which ended up looking very similar to his, minus merging the body back into the request, which I still don't completely understand the purpose of.
My solution is now simply to not use multipart/form-data and instead use application/x-www-form-urlencoded. I don't know if this decision will haunt me later on down the road but it solves my problem for now. I can still use this for my API as in JavaScript I can specify the content type as such:
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
I'd be very interested in knowing why PHP doesn't add a $_PUT superglobal?

Python to PHP on server send image

I have a raspberry pi running a lamp stack, an arduino and a camera hooked up. The end goal is that when my arduino takes a photo, it then writes an image to a php address which is then emailed.
Right now, I'm trying to get the image to get placed in the right place.
Here's my php snippet:
<?php
print_r($_FILES);
move_uploaded_file($_FILES["file"]["tmp_name"], "/var/www/images/mypic.jpg");
?>
My python code is doing:
import requests
r = requests.get('https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png')
r2 = requests.post('http://192.168.1.100/accept_image.php', data = r.content)
I realize the image is going to get overwritten. That's not a problem. I can always add a timestamp later etc etc.
However, this gives me an error code. I'm a beginner at php and use python mainly for scientific computing so not sure if I'm passing the picture correctly. I know that the ip is correct as I can connect to it and it's all in network.
I have looked at this Python script send image to PHP but am still getting stuck.
EDIT:
Upon further debugging:
print_r($_POST);
returns an empty array. Not sure why?
To have a file accessible to PHP in $_FILES, you must use HTML-form style encoding of files (multipart/form-data). This is different from a standard POST request including the content in the request body. This method is explained in http://php.net/manual/en/features.file-upload.post-method.php - the key here is:
PHP is capable of receiving file uploads from any RFC-1867 compliant browser.
What's you're trying to do is not sending it the RFC-1867 way, but a plain-old POST request.
So you have two options:
Send the data from Python using multipart/form-data encoding. It shouldn't be too hard but requires some work on the Python side.
Just grab the data on the PHP side not from $_FILES but by directly reading it from the POST body, like so:
.
$data = file_get_contents('php://input');
file_put_contents("/var/www/images/mypic.jpg", $data);
It's more of a memory hog on the PHP side, and means you need to do some validation that you actually got the data, but is quite simpler.
To clarify, in PHP $_POST is only populated when the Content-type request header is multipart/form-data or application/x-www-form-urlencoded (and of course the data is encoded in the proper way).
If you get a POST request with anything else in the body, you can read it directly by reading from the php://input stream, and you're responsible for handling / decoding / validating it.

Content type in codeigniter

I have a controller in CodeIgniter which have few functions in it. For ajax interaction, in some functions where i had to return json objects, i did the following :
$this->output->set_content_type("application/json");
The ajax is working fine. But now when i'm trying to echo something from a function and going to that function in browser gives me the following :
Content Encoding Error
The page you are trying to view cannot be shown because it uses an invalid or unsupported form of compression.
I tried setting content type to "text/html", i don't know. Also i tried removing all those content type set. That also didn't help me echoing out of the controller.
What should i do, to echo something out of a specific controller function just in case for testing purposes?
You seem to have GZIP compression turned on in your config. it might mess with your AJAX transfers and/or caching.
By default you don't need to give a content-type when serving json encoded data. So it's better to leave the whole content-type out to prevent future issues. Besides that it's also handy because there can be occasions where you might have one AJAX call give either a JSON encoded string or just a piece of output.
Good luck!

Some markup mysteriously goes missing when send to server using ajax (POST) as string

OS: Windows 7 (64-Bit)WAMP Version: 2.2Apache Version: 2.2.22PHP
Version: 5.4.3Framework: CodeIgniter 2.1.2
Hi, I am experiencing something strange, and can't seem to pinpoint the cause. I am sending a js object to server to be saved in db in BLOB. The object is converted to JSON string using JSON.stringify() before sending it in Ajax(POST) call.
Object After Using JSON.stringify
[{"page_id":"1","site_id":456,"composite_id":"456-1-text-1","type":"2","properties":"{\"id\":\"text-1\",\"isModified\":false,\"isNew\":false,\"name\":\"Text 1\",\"content\":\"<span style=\\\"text-decoration:underline;font-size:x-large;color:#7092be;background-color:#c8bfe7;\\\"><strong>Your text here</strong></span>\",\"keywords\":\"\",\"top\":103,\"left\":119,\"width\":130,\"height\":30,\"style\":{\"borderColor\":\"#1e07da\",\"borderSize\":\"2\",\"borderRadius\":\"6\",\"shadowType\":\"drop-shadow\",\"shadowColor\":\"#000000\",\"shadowSize\":\"0\",\"shadowBlur\":\"0\",\"boxShadowColor\":\"#000000\",\"boxShadowPosX\":\"3\",\"boxShadowPosY\":\"2\",\"boxShadowBlur\":\"5\",\"boxShadowType\":\"\",\"zIndex\":2020}}"}]
This is the concerning part from above string that gets modified(Key-Value Pair):
\"content\":\"<span style=\\\"text-decoration:underline;font-size:x-large;color:#7092be;background-color:#c8bfe7;\\\"><strong>Your text here</strong></span>\"
I send this string to api controller using ajax. When I receive it in the controller I use print_r() to print the variable. But now insted of the above mentioned part this is what I receive:
[{"page_id":"1","site_id":456,"composite_id":"456-1-text-1","type":"2","properties":"{\"id\":\"text-1\",\"isModified\":false,\"isNew\":false,\"name\":\"Text 1\",\"content\":\"<span ><strong><em>Your text here</em></strong></span>\",\"keywords\":\"\",\"top\":103,\"left\":119,\"width\":130,\"height\":30,\"style\":{\"borderColor\":\"#1e07da\",\"borderSize\":\"2\",\"borderRadius\":\"6\",\"shadowType\":\"drop-shadow\",\"shadowColor\":\"#000000\",\"shadowSize\":\"0\",\"shadowBlur\":\"0\",\"boxShadowColor\":\"#000000\",\"boxShadowPosX\":\"3\",\"boxShadowPosY\":\"2\",\"boxShadowBlur\":\"5\",\"boxShadowType\":\"\",\"zIndex\":2020}}"}]
As you can see the concerned part is missing the style settings and is now modified to:
\"content\":\"<span ><strong><em>Your text here</em></strong></span>\"
I have been searching it over the internet, but it does not seem to be a common problem as I cant find anything related to it. So now I am counting on SO.
Regards
UPDATE:
I used var_dump($_REQUEST)as suggested by #BogdanBurim. And this is the concerned part:
\"content\":\"<span style=\\\"text-decoration:underline;font-size:x-large;color:#7092be;background-color:#c8bfe7;\\\"><strong>Your text here</strong></span>\"
As you can see the style settings are still there. So with we come to the decision that CI filters might be removing this part from string. So now the question is how to fix this issue?
Well after discussion with #BogdanBurim, I came to conclusion that to avoid XSS to some extent CI removed any text included within quotes and double quotes, from data sent to server.
There was an option to extend CI_Input library to allow selective disabling of XSS filter. That would solve my issue but would raise security concerns. So instead what I did is I encode (base_64) the data before sending it to the api. It is saved in DB as BLOB. And when fetched is decoded on client side.

PHP get PUT request body

I'm currently developing a Restful Json-API in PHP. I want to send a PUT-Request to items/:id to update a record. The data will be transferred as application/json.
I want to call the API with
curl -H "Content-Type: application/json" -X PUT -d '{"example" : "data"}' "http://localhost/items/someid"
On the server side, I'm not able the retrieve the request body. I tried
file_get_contents("php://input");
but this returns an empty string. Also a fopen()/fread() combination doesn't work.
When calling via POST, everything works great, I can read the json perfectly on the server side. But the API isn't Restful anymore. Does anyone have a solution for this? Is there another way to send and receive Json?
btw, I'm developing the API with the Slim Framework.
php://input is only readable once for PUT requests:
Note: A stream opened with php://input can only be read once; the stream does not support seek operations. However, depending on the SAPI implementation, it may be possible to open another php://input stream and restart reading. This is only possible if the request body data has been saved. Typically, this is the case for POST requests, but not other request methods, such as PUT or PROPFIND.
http://php.net/manual/en/wrappers.php.php
The Slim framework already reads the data upon request. Take the data from the Request object, into which it has been read.
On the server side, I'm not able the retrieve the request body. I tried file_get_contents("php://input");
You can only use file_get_contents( 'php://input', 'r' ); once per request. Retrieving its values will truncate the values as well, so if you call it twice, it'll return an empty string. Slim's request object contains the values you need, so:
<?php
$app = new Slim( );
$app->put( '/items/someid', function () use ( $app ) {
echo $app->request( )->put( 'example' ); // should display "data".
});
The example from the PHP manual uses fopen to access php://input in read mode. Have you tried doing it that way instead?
EDIT: The manual page for PHP:// says some stuff that seems to suggest that PUT data might not be available in some cases!
Note: A stream opened with php://input can only be read once; the
stream does not support seek operations. However, depending on the
SAPI implementation, it may be possible to open another php://input
stream and restart reading. This is only possible if the request body
data has been saved. Typically, this is the case for POST requests,
but not other request methods, such as PUT or PROPFIND.
I don't know where this will leave you regarding PUT processing. One page seems to say it's possible, the other seems to imply that it won't work under the wrong set of circumstances
I was reading the SLIM framework documentation the other day and it said that some browsers have problems with PUT and DELETE.
Excerpt:
Unfortunately, modern browsers do not provide native support for PUT requests. To work around this limitation, ensure your HTML form’s method is “post”, then add a method override parameter to your HTML form like this:
<form action="/books/1" method="post">
... other form fields here...
<input type="hidden" name="_METHOD" value="PUT"/>
<input type="submit" value="Update Book"/>
</form>
Source: http://www.slimframework.com/documentation/stable

Categories