According to php manual nor php://input neither $HTTP_RAW_POST_DATA work with multipart/form-data POST-requests.
"php://input allows you to read raw POST data. It is a less memory intensive alternative to $HTTP_RAW_POST_DATA and does not need any special php.ini directives. php://input is not available with enctype="multipart/form-data"."
How can I get raw data for multipart/form-data forms?
Direct answer: you can not do that. PHP insists on parsing it itself, whenever it sees the multipart/form-data Content-Type. The raw data will not be available to you. Sadly. But you can hack around it.
I hit a similar problem, a partner was sending incorrectly formatted data as multipart/form-data, PHP could not parse it and was not giving it out so I could parse it myself.
The solution? I added this to my apache conf:
<Location "/backend/XXX.php">
SetEnvIf Content-Type ^(multipart/form-data)(.*) NEW_CONTENT_TYPE=multipart/form-data-alternate$2 OLD_CONTENT_TYPE=$1$2
RequestHeader set Content-Type %{NEW_CONTENT_TYPE}e env=NEW_CONTENT_TYPE
</Location>
This will change the Content-Type of incoming request to XXX.php from multipart/form-data to multipart/form-data-alternate, which is enough to block PHP from trying to parse it
After this you can finally read the whole raw data from php://input and parse it yourself.
It is ugly, but I have not found a better or in fact any other solution - short of asking the partner to fix their side.
NB! When you do what I described here, $_FILES will be empty.
You can set enable_post_data_reading = Off and PHP won't intercept multipart/form-data data.
Requires: PHP 5.4
I didn't implement this fully, but it looks like it should work. In Apache conf:
SetEnvIf Content-Type ^(multipart/form-data)(.*) MULTIPART_CTYPE=$1$2
RequestHeader set Content-Type application/x-httpd-php env=MULTIPART_CTYPE
RequestHeader set X-Real-Content-Type %{MULTIPART_CTYPE}e env=MULTIPART_CTYPE
Setting the Content-Type to application/x-httpd-php appears to solve the original problem of PHP parsing the body, and the problem Norbert Farkas reported: "Apache sends back PHP source code". The body is then available on php://input, and the real content type in the X-Real-Content-Type header. (That header may not be necessary for you -- the MULTIPART_CTYPE variable didn't seem to be showing up in my $_ENV, but the new header did.) All other requests should be handled as usual.
Thanks to Anti Veeranna for most of it! :)
EDIT: P.S. Obviously it's Apache-specific, but in some of the other configurations of PHP there may very well be easier ways.
//Get the raw POST data
$postBody = file_get_contents("php://input");
Related
Is there an alternative?
I'm using Advanced Rest Client for testing an API I'm developing.
I send a JSON with POST.
In code, $_FILES is fine, but file_get_contents("php://input") is empty.
If I don't send any files, then I can use file_get_contents("php://input")
PHP version: 5.6.4
As GhostGambler states, php://input is not available with enctype="multipart/form-data".
You should not attach the JSON as a file to your request, you should add it as the request body to the post request, setting the Content-Type header (application/json). Then it will be available in php://input.
Ok, so I ended up giving a name to my JSON data, like 0=[{"q":"w"}] and then get it with $_POST['0']. And the files with $_FILES
Here's how it looks in Advanced REST Client:
Most likely accessing any of the POST/FILES superglobals consumes php://input.
In any case, if you send a JSON payload you cannot have a multipart-formdata payload too so $_FILES should be empty. If you need to handle both on the same page (bad idea IMO) make sure to check the content type header or some other information outside the request's body before accessing either $_FILES or php://input
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".
http://php.net/manual/de/wrappers.php.php
Since HTTP_RAW_POST_DATA is marked deprecated, I guess you are somewhat unlucky. I do not know alternatives.
Edit: Well, you could try php://stdin / STDIN, although I do not know if this works with PHP in a webserver ... maybe just try it out.
I'm not sure if the problem occurs because of wrong PHP-code or maybe a wrong configuration of nginx.
I like to generate a feed in atom-format. The XML of the feed is valid. I do set the content-type via
header("Content-type: application/atom+xml");
before I put out the XML. Nonetheless, I get different information from chromium developer-tools.
The tableview in Network shows me text/plain as type:
However, the header itself seems okay as it states application/atom+xml:
This mime-type is correctly set inside nginx-configuration:
types {
[...]
application/atom+xml atom;
[...]
}
What could be missing/wrong that chromium does not recognize the correct mime-type of my feed and states it as text/plain?
The problem seems to be Chrome not regognizing the application/*+xml content type. It looks that you need to use plain old application/xml to get XSLT processing and correct Content-Type display to work in dev tools.
I'm trying to access the raw HTTP request sent to the server in PHP.
However, all the input/output streams are not working.
I can't use php://input, and I don't want to have to "interpolate" the request from the arrays such as $_COOKIES, $_POST, etc. $_POST, $_GET and the other arrays are working fine. I'm using WAMPServer on Windows 7.
Can anyone help me fix the problem with the input/output streams or find another way to get the raw request data?
From the PHP docs:
php://input is a read-only stream that allows you to read raw data from the request body
which means you can only read body data, not headers or the raw request. If you're running under Apache, you can use the function apache_request_headers to get all the headers. To get the "request" line (the first line of the request), I suppose you need to concat the strings you can get from the $_SERVER variable.
I am quite confused with the Content-Type vendor specific. Say by default,
Content-Type: application/json
but with vendor specific type, I can have this
Content-Type: application/vnd.anything.process-v1+json
Do I need to have any special function to do in PHP to use if I would check if the Content-Type is not vendor specific?
Thanks.
If you are accepting content via HTTP POST from a client you can check the content type with $_SERVER['CONTENT_TYPE']. I would encourage you to use the #ver attribute in the Content-Type header instead of embedding in your vendor content type name. Example:
Content-Type: application/vnd.anything.process+json;ver=1
If you are sending content to a server via HTTP POST you need to specify an accept header to tell the client to return the data in the vendor specific content type. Something like this would work:
header('Accept: application/vnd.anything.process+json;ver=1');
Accept headers can be quite complicated though. If you do not control the server you are posting to as client, you should provide a sensible default to your Accept header. Something like this is more friendly if you want the server to send back plain JSON if it doesn't understand the vendor header:
header('Accept: application/vnd.anything.process+json;ver=1;q=0.9, application/json;q=0.1');
The most permissive Accept headers accept anything though:
header('Accept: application/vnd.anything.process+json;ver=1;q=0.9, application/json;q=0.5, */*;q=0.1');
Here is the actual RFC for Accept headers: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
A peer of mine is developing an iPhone application that will allow users to post images on my site via my API. I am building the part of the API that will accept and process the images.
The mobile developer is sending headers like such:
Content-Disposition: form-data; name="photo_1"; filename="photo_1.jpg"
Content-Type: application/octet-stream
When looking for the images sent, is it the same method as with normal HTML forms? Should I look for $_FILES?
Or, using PHP, how would I find his image?
Doesn't appear it's being sent via a form, i.e., <form enctype=multipart/form-data"> and <input type="file">, so the $_FILES array won't be populated.
You'll probably need to read:
$HTTP_RAW_POST_DATA
or do:
$rawPost = file_get_contents("php://input");
From the manual:
php://input allows you to read raw
data from the request body. In case of
POST requests, it preferrable to
$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".
For more info, check out:
http://php.net/manual/en/wrappers.php.php
http://php.net/manual/en/reserved.variables.httprawpostdata.php
I suppose iOS is sending the whole file as a single block of data in the POSTDATA section of the HTTP request. You can retrieve the whole POSTDATA (not parsed):
<?php
$postdata = file_get_contents("php://input");
?>
$_FILES is meant for reading files sent with enctype="multipart/form-data" in a proper HTML form. iOS is probably sending a plain old POST containing just a bunch of bytes which represent the file.
Tell me if this solves!
See these answers I gave to similar questions (processing uploads from php://input):
userland multipart/form-data handler and also
How to validate if uploaded file is an image? [file sent via HTML5's File API, received via php://input]