So I am stumbling a bit here, as I have figured out that PHP will not read the HTTP request body from a PUT request. And when the Content-Type header in the request is set to application/json, there doesn't seem to be any way to get the body.
I am using Laravel, which builds their request layer on top of Symfony2's HttpFoundation lib.
I have debugged this a bit with jQuery, and these are some example requests:
Doing a request like this, I can find the content through Input::getContent()
$.ajax({
url: 'http://api.host/profiles/12?access_token=abcdef',
type: 'PUT',
data: {"profiles":[{"name":"yolanda ellis","email":"yolanda.ellis12#example.com"}]}
});
I cannot get the content with file_get_contents('php://input') though. jQuery per default sends the data as application/x-www-form-urlencoded.
It becomes even more mindboggeling when I pass another Content-Type in the request. Just like Ember-Data does:
$.ajax({
url: 'http://api.host/profiles/12?access_token=abcdef',
type: 'PUT',
data: {"profiles":[{"name":"yolanda ellis","email":"yolanda.ellis12#example.com"}]},
contentType: 'application/json'
});
The data seems nowhere to be found, when doing it like this. This means that my Ember.js app does not properly work with my API.
What on earth is going on here?
Edit
Here's a full request example as seen in Chrome DevTools: http://pastebin.com/ZEjDAsmJ
I have found that this is a Laravel specific issue.
Edit 2: Answer found
It appears that there's a dependency in my project, which reads from php://input when the Content-Type: application/json header is sent with the request. This clears the stream—as pointed out in the link provided by #Mark_1—causing it to be empty when it reaches Laravel.
The dependency is bshaffer/oauth2-server-php
You should be able to use Input::json() in your code to get the json decoded content.
I think you can only read the input stream once, so if a different package read the input stream before you, you can't access it.
Are you using OAuth2\Request::createFromGlobals() to create the request to handle your token? You should pass in the existing request object from Laravel, so both have access to the content.
Did you read this? http://bshaffer.github.io/oauth2-server-php-docs/cookbook/laravel/
That links to https://github.com/bshaffer/oauth2-server-httpfoundation-bridge which explains how to create a request object from an httpfoundation request object (which Laravel uses).
Something like this:
$bridgeRequest = \OAuth2\HttpFoundationBridge\Request::createFromRequest($request);
$server->grantAccessToken($bridgeRequest, $response);
So they both share the same content etc.
I found the following comment at http://php.net/manual/en/features.file-upload.put-method.php
PUT raw data comes in php://input, and you have to use fopen() and
fread() to get the content. file_get_contents() is useless.
Does this help?
Related
I am trying to send an HTTP PUT request with "Content-Type": "multipart/form-data" to a Laravel application. When I change the method to POST it works.
$a = $request->all(); // With PUT this is empty but with POST it works fine.
The client-side executes the following code:
axios({
method: "post", // when I try method:"PUT" and change the content type
url: "/api/offer",
data: fd,
headers: {"Content-Type": "multipart/form-data"} // here change to "x-www-form-urlencoded" it the $a array on backend is empty!
}).then(response => {
console.log("/offer/" + response.data)
if (response.data)
window.location.replace("/offer/" + this.offer.id);
else {
console.log("show a message that something went wrong! ")
}
}).catch(function (error) {
})
I could not find anywhere in the docs that PUT can't send "multipart/form-data"
So, can PUT send "multipart/form-data" or only POST can do that in general or it is only a PHP / Laravel Issue?
Edit:
Also, what difference does it make to use PUT instead of POST other than to comply with HTTP protocol and CRUD operation properly?
Laravel (HTML Forms) do not work great with Put requests so you'll need to spoof a POST request as if it was a PUT or PATCH request. On Axios you use the .post verb but within your form data you append
_method: "put"
Information from the official documentation:
https://laravel.com/docs/8.x/routing#form-method-spoofing
Excerpt from the documentation:
HTML forms do not support PUT, PATCH, or DELETE actions. So, when defining PUT, PATCH, or DELETE routes that are called from an HTML form, you will need to add a hidden _method field to the form. The value sent with the _method field will be used as the HTTP request method
I ran into this issue a few weeks ago myself with a Symfony 5.3 project. It only worked with POST Requests, not with PUT. Here's an issue from the Symfony GitHub that explains it in more detail.
To my understanding the issues lies within the PHP implementation of those requests. The HTTP standard "PUT" supports it, but PHP does not. Here's also a link to the bug from the PHP bugtracker.
Errors appear when I encrypt and upload the feed data
the document link :https://github.com/amzn/selling-partner-api-docs/blob/main/guides/use-case-guides/feeds-api-use-case-guide-2020-09-04.md#step-2-encrypt-and-upload-the-feed-data
I develop in php,and the composer is composer require double-break/spapi-php
$feeder = new Feeder();
$feeder->uploadFeedDocument($docPayload, 'text/plain; charset=utf-8',
//ROOT_PATH.'uploads/amz/'.$feedFileName
'https://images-na.ssl-images-amazon.com/images/G/01/rainier/help/xsd/release_4_1/OrderAcknowledgement.xsd'
);
Errors appear when I encrypt and upload the feed data:
Make sure content type you pass to createFeedDocument matches exactly the content type you pass to Feeder::uploadFeedDocument. In my case I was passing text/tab-separated-values to the former but text/tab-separated-values; charset=UTF-8 to the latter (with charset appended) and was getting the error you're describing. I fixed it by passing text/tab-separated-values; charset=UTF-8 in both instances.
I agree with this comment https://stackoverflow.com/a/67474344/12360781
But a more elaborative answer would be that we should pass these headers to the PUT request and of course the content-type should be the same as we passed to CreateFeedDocument operation.
{"Content-Type": "text/tab-separated-values; charset=UTF-8"}
This piece of code worked for me in Ruby:
faraday_connection = Faraday::Connection.new(#url)
#response = faraday_connection.send(:put, nil, #data.to_json, { "Content-Type": "text/tab-separated-values; charset=UTF-8" })
I had the same issue and after spending three days searching for the solution at last I come up with the following solution although I was using the Github C# sdk but the error was the same.
I was missing two important header required and after providing them fixed the issue for me.
Following is the code (C#)
restRequest.AddOrUpdateHeader("Content-Type", "application/json");
restRequest.AddOrUpdateHeader("User-Agent", "XXXX 2022");
Best of Luck!!!
I've been trying to figure out what's really the usage of header('Content-Type: application/json') in php scripts and I've found different questions and answers on stackoverflow about this subject but I still don't completely get it...
So here's the question : I've seen in some php projects this line of code, and I'm trying to understand
if this is used when another web page is calling this actual script (with ajax for example) so that the calling page can get a json from the php page
OR
if this script means that the php page is going to deal with json sent from another web page. Or maybe something else ???
Another thing that could help me if answered, lately I've been retrieving json from a resource (external url) with cURL and I had to put this header (Content-type:application/json) in the request. Did I send this header to the exertnal resource or was this MY header so that I can deal with the returned json ?
thanks
Ok for those who are interested, I finally figured out that header('Content-Type: application/json') is used when another page is calling the php script, so that the other page can automatically parse the result as json.
For instance i have in my test.php :
header('Content-type: application/json; charset=utf-8');
$arr = array ('a'=>1,'b'=>2,'c'=>3,'d'=>4,'e'=>5);
echo json_encode($arr); // {"a":1,"b":2,"c":3,"d":4,"e":5}
and in my main.js
function test() {
$.ajax({
url: 'test.php',
type: 'GET',
//dataType: 'html',
success: function (response) {
alert(response);
}
});
};
When I dont have dataType set to "json" or when I don't have the header in my test.php, the alert gives {"a":1,"b":2,"c":3,"d":4,"e":5} which is a string (tried with typeof(response), and when I have this header, or dataType:"json", I get [object Object] from the alert. So this header function is there to indicate to the calling pages which type of data it gives back, so that you can know how to deal with it. In my script, if I didn't have header('Content-Type: application/json'), I would have to parse the response in the javascript like this : JSON.parse(response) in order to make it a json, but with that header, I already have a json object, and I can parse it to html with jSON.stringify(response).
You should always set the Content-Type for any HTTP response to describe what you're serving in that response.
Whether it's JSON or something else, and whether it's for an AJAX request or any other kind of request.
You should also set the Content-Type for any request to describe your POST payload.
In PHP, if you don't specify the Content-Type header in the script, it will default to whatever you've configured default-mimetype to be in your php.ini file which is usually text/html.
Calling header('Content-Type: application/json') will override that default setting so that the script will respond with that Content-Type when requested.
Also, when calling curl with a Content-type:application/json header, you're specifying the content type for your request body and not for the expected reponse.
W3 Description For the Content-Type
The purpose of the Content-Type field is to describe the data contained in the body fully enough that the receiving user agent can pick an appropriate agent or mechanism to present the data to the user, or otherwise deal with the data in an appropriate manner.
Shortly speaking, just to inform the receiver what kind of data he received and consume it accordingly.
Is this it?
I am trying to convert, $data = file_get_contents("php://input"); to classic asp...
Set xmlhttp = CreateObject("MSXML2.ServerXMLHTTP.6.0")
xmlhttp.open "GET", php://input, false
xmlhttp.setRequestHeader "Content-type", "application/x-www-form-urlencoded"
xmlhttp.send
TOKEN = xmlhttp.responseText
edit: Answering John's question...
Realtime Updates
Following a successful subscription, Facebook will proceed to call
your endpoint every time that there are changes (to the chosen fields
or connections). For each update, it will make an HTTP POST request.
The request will have content type of application/json and the body
will comprise a JSON-encoded string containing one or more changes.
Note for PHP developers: In PHP, to get the encoded data you would use
the following code:
$data = file_get_contents("php://input");
$json = json_decode($data);
Edit #2
This is an educated guess based on your Facebook info - try
Set xmlhttp = CreateObject("MSXML2.ServerXMLHTTP.6.0")
xmlhttp.open "GET", Request, false
xmlhttp.setRequestHeader "Content-type", "application/json"
xmlhttp.send
TOKEN = xmlhttp.responseText
Basically this is your original idea with a little change in line 2 and another in line 3. You could also try Request.Form rather than Request in line 2 as the script is receiving POST data
Edit - yes, it looks like your code will work, with one minor change. Your URL needs to go inside double quotes - ie
xmlhttp.open "GET", "php://input", false
Thanks for the question. I've learned something today. I'll leave my original answer as background reading
Could you tell me a bit more about what you are trying to achieve. It looks like you want to take the content of an external URL and then use it in your ASP page. You can certainly use an XML object provided that the output of your external URL is valid XML. The code looks like this.
set xml = Server.CreateObject("Msxml2.DomDocument")
xml.setProperty "ServerHTTPRequest", true
xml.async = false
xml.validateOnParse = false
xml.load("http://yoururl")
You then have an xml object, here just called "xml" which you can use however you need. For example if you just want it to appear in the page as is you would add
Response.write xml
If your external URL output is not valid XML then I don't think Classic ASP can't do this on its own, you may need to install a third party component on your server, such as AspTear
http://www.alphasierrapapa.com/ComponentCenter/AspTear/
The code you suggest above, or a variation on it, might well work, I'm going to experiment with it. Classic ASP itself has had no updates for more than a decade but Microsoft's XML processor certainly has been updated
I want to use the google images api. In the past when I worked with json I simply used the ajax function to get the json from my own server. But now I will be getting it from an external domain:
https://ajax.googleapis.com/ajax/services/search/images?q=fuzzy monkey&v=1.0
Obviously I can't load this using js since its not from an internal url. So in these cases how does one work with json data. Are you supposed to load it via CURL using a server side script or is there another way?
You can make use of JSONP by adding a callback GET param.
https://ajax.googleapis.com/ajax/services/search/images?q=fuzzy%20monkey&v=1.0&callback=hello
Then you can request it with jQuery's $.getJSON().
$.getJSON('https://ajax.googleapis.com/ajax/services/search/images?q=fuzzy%20monkey&v=1.0&callback=?', function(response) {
console.log(response.responseData);
});
jsFiddle.
You must use Cross Origin Resource Sharing (CORS http://en.wikipedia.org/wiki/Cross-Origin_Resource_Sharing)
It's not as complicated as it sounds...simply set your request headers appropriately...in Python it would look like:
self.response.headers.add_header('Access-Control-Allow-Origin', '*');
self.response.headers.add_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
self.response.headers.add_header('Access-Control-Allow-Headers', 'X-Requested-With');
self.response.headers.add_header('Access-Control-Max-Age', '86400');