5

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

Ronni Egeriis Persson
  • 2,209
  • 1
  • 21
  • 43
  • Any particular reason this is a PUT request and not a POST request? – Powerlord Aug 19 '14 at 15:24
  • any chance that query string parameters from `http://api.host/profiles/12?access_token=abcdef` do something nasty here? Also, have you checked out [this thread](http://laravel.io/forum/02-13-2014-i-can-not-get-inputs-from-a-putpatch-request)? – reafle Aug 19 '14 at 15:30
  • @Powerlord I have build the API around jsonapi.org. POST = create, PUT = alter. – Ronni Egeriis Persson Aug 20 '14 at 11:48
  • To a clarify a bit further on this old answer: if you just remove the contentType from the post it will work. – R. Flierman Feb 03 '20 at 15:55

2 Answers2

7

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.

Barryvdh
  • 6,419
  • 2
  • 26
  • 23
3

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?

Mark_1
  • 623
  • 6
  • 16
  • Unfortunately that doesn't seem to work either. I can't seem to find a case where PUT request payload is available through `php://input` – Ronni Egeriis Persson Aug 20 '14 at 12:29
  • Do you have a content-length header? The same item http://php.net/manual/en/features.file-upload.put-method.php#59720 says The HTTP PUT request MUST contain a Content-Length header to specify the length (in bytes) of the body, or the server will not be able to know when the input stream is over. This is the common problem for many to find the php://input empty if no such header available. Edited again to make the comment readable – Mark_1 Aug 20 '14 at 12:49
  • Yes. I have found that it is a Laravel specific issue. Just did a vanilla PHP script which had no issue reading the request payload. – Ronni Egeriis Persson Aug 20 '14 at 12:51