I am attempting to write a RESTful service using CakePHP 2.3.5. So far I've successfully created the GET functions for the resource I'm working with. I can send a GET request to example.com/areas.json or to example.com/areas/1.json and it returns the data in my database.
However, I started trying to get the edit function working. I wrote a simple edit method that simply saved the incoming data from $this->request->data. I'm using Postman to test the functionality and sending raw JSON over PUT or POST to example.com/areas/1.json returns a message telling me that the data couldn't be saved. I made the method send me more information when it failed and it tells me that there is no incoming data in either $this->request->data or $this->data.
I've been searching the Internet for solutions to this or similar problems, but everything I have tried has failed so far. I've attempted disabling CSRF checks, disabling the SecurityComponent altogether, and multiple other fixes all involving the security. Changing any of those resulted in black holing the request.
Does anyone have any thoughts on what else I could try to get CakePHP to accept the JSON data into a request? I'll include my edit function below in case that helps.
public function edit($id)
{
$this->Area->id = $id;
$message['request-data'] = $this->request->data;
if ($this->Area->save($this->request->data)) {
$message['response'] = $this->Area->findById($id);
} else {
$message['response'] = "Error";
}
$this->set(array(
'message' => $message,
'_serialize' => array('message')
));
}
First, make sure the Content-Type of the request is application/json.
Second, CakePHP doesn't automatically decode the JSON payload; you have to do it manually. From the manual:
// Get JSON encoded data submitted to a PUT/POST action
$data = $this->request->input('json_decode');
Related
I have been writing an API using Symfony as the backend, a plugin written by a third party is sending certain data to an endpoint, the endpoint is then to return a json encoded response, however following the instructions as set out in the current symfony documaentation(https://symfony.com/doc/current/components/http_foundation.html) the return value is displayed twice and the response is not well formed and outputs like string
The original method that I wrote had calls to a database to validate a token, store a bookmark and display the result of the backend process, however when getting down to brass tacks and removing everything but the response building; it is obvious that this is where the problem lies. The method uses this snippet, though for clarity I have not included the database processing and used the posted values as the return array, the result is the same if it is the post or processed data, the output displays twice.
$token = $request->request->get('token');
$bookmark = $request->request->get('bookmark');
$data = ['token' => $token, 'bookmark' => $bookmark];
$response = new Response();
$response->headers->set('Access-Control-Allow-Origin', '*');
$response->setContent(json_encode($data));
$response->send();
return $response;
What I was expecting was a single json response but what is returned is a double string of the json output
e.g. if I posted these values to the above snippet:
token: ksjdbvqpi8e7rqp7evbprb
bookmark: http://www.google.com
the return result is
{"token":"ksjdbvqpi8e7rqp7evbprb", "bookmark":"http:\/\/www.google.com"}{"token":"ksjdbvqpi8e7rqp7evbprb", "bookmark":"http:\/\/www.google.com"}
when what I was expecting was just
{"token":"ksjdbvqpi8e7rqp7evbprb", "bookmark":"http:\/\/www.google.com"}
I have no idea at the moment why it is displaying twice, any help is as always appreciated.
Thanks
$response->send(); is the line that should be removed.
As you already return object of class Response symfony will take care to output this response to browser, you don't need to do it manually with send().
I've got this very odd problem...on my remote web server for this specific application, when I try to receive JSON input (in any way) I either get a NULL or empty ("") return. The odd part about this problem, is that it is working fine on my local machine. And when I say "working fine" what that means is that I can make a request via Postman to my local machine's API endpoint and it does what it's supposed to do. The other odd thing is that on this very web server (in different files, but still this web server), I can receive JSON input just fine.
I have tried different ways of sending the JSON data to this remote server as well. I have raw cURLed the data, I have sent it from Postman, I have sent it from the webhooks app in Zapier, and I have sent it from a different application's webhook functionality. All of these different applications have given me a NULL return on trying to retreive the JSON data. Here's how I'm trying to retrieve it:
// index.php, the API endpoint processing file
<?php
declare(strict_types=1);
require_once '../../vendor/autoload.php';
require_once '../../model/model.php';
use ShinePHP\{Crud, CrudException, EasyHttp, EasyHttpException, HandleData, HandleDataException};
try {
$data = EasyHttp::turnJsonInputIntoArray();
} catch (EasyHttpException $ehe) {
echo json_encode(['status' => 'failure', 'message' => $ehe->getMessage()]);
exit;
}
And this is the turnJsonInputIntoArray() method in the EasyHttp class:
public static function turnJsonInputIntoArray(string $urlToRetrieveFrom = 'php://input') : array {
// Check if JSON is null, if it is, throw EasyHttpException, if not, return the decoded assoc array.
if (json_decode(file_get_contents($urlToRetrieveFrom), true) === null) {
throw new EasyHttpException('No data retrieved from url: '.$urlToRetrieveFrom);
} else {
return json_decode(file_get_contents($urlToRetrieveFrom), true);
}
}
I've var_dumped $_POST (it returned an empty array), $HTTP_RAW_POST_DATA (returned NULL), and file_get_contents('php://input') (returned an empty string). Nothing exists in the nginx error log aside from the custom exception that exists because there is nothing in php://input. Nothing has worked and I really don't know how else to receive my JSON data.
How can I receive JSON data into my PHP script running on a remote server?
I'm trying to create a "service" like application, which can be able to receive API calls from another services. (These services will be built, for different purposes). And also able to send API calls to an another one.
Each request that they send, and accept has to have the following format.
{
header : {
// some header information, like locale, currency code etc.
signature : "some-hashed-data-using-the-whole-request"
},
request : {
// the usable business data
}
}
To each request I want to append a hash, that is generated from the actual request or anyhow (salted with password or any kind of magic added). Its not that important at the moment. I gave the name signature to this field. So for each received request, I want to reproduce this signature from the request. If the signature I received is matching with the one I generated, I let the application run otherwise showing some error message.
I already read a few articles, but most of them is for user-pass combinations.
My question is not about that if it's a good solution or not. I just want to know how can implement a middleware like functionality - like in laravel - in Symfony 4?
Instead of putting headers into a JSON object the HTTP body, use HTTP headers directly. That’s what they are for. When you’re using non-standard headers, prefix them with X- and maybe a prefix for your application, for example X-YourApp-Signature. The request goes into the body, i.e. the value of the request property in your example.
The server side is pretty simple with Symfony:
public function someAction(Request $request)
{
$signature = $request->headers->get("X-YourApp-Signature");
$data = json_decode($request->getContent());
// ... go on processing the received values (validation etc.)
}
If you want to write a HTTP client application in PHP, I would recommend using the Guzzle library. Here’s an example:
$headers = ["X-YourApp-Signature" => "your_signature_string"];
$data = json_encode(["foo" => "bar"]);
$request = new \GuzzleHttp\Psr7\Request("POST", "https://example.com", $headers, $data);
$client = new \GuzzleHttp\Client();
$response = $client->send($request, ["timeout" => 10]);
var_dump($response);
Of course, you’ll also want to implement some error handling etc. (HTTP status >= 400), so the code will be a bit more complex in a real application.
As k0pernikus mentioned, the before after filters solves my issue.
I am trying to send a PUT request method from my Android app to my PHP endpoint but in my endpoint the PUT request is not recognized as a PUT request so I return Request method is wrong! message from my endpoint.
Android interface and request execution
Interface for activation
#PUT("device/activate.php")
Call<DeviceRegistry> registryDevice();
Executing the request
DeviceRegistryAPI registryAPI =
RetrofitController.getRetrofit().create(DeviceRegistryAPI.class);
Call<DeviceRegistry> registryCallback = registryAPI.registryDevice();
response = registryCallback.execute();
With this I am expecting a response but I am getting my endpoint error message.
My PHP endpoint
if($_SERVER['REQUEST_METHOD'] == "PUT"){
//doing something with the data
} else {
$data = array("result" => 0, "message" => "Request method is wrong!");
}
I don't know why the $_SERVER['REQUEST_METHOD'] == "PUT" is false but I wonder if I am missing something on Retrofit 2.
More Info.
I am using Retrofit2.
Update 1: Sending json into the body
I am trying to send a json using the body.
It is my json:
{
"number": 1,
"infoList": [
{
"id": 1,
"info": "something"
},
{
"id": 2,
"info": "something"
}
]
}
There are my classes:
class DataInfo{
public int number;
public List<Info> infoList;
public DataInfo(int number, List<Info> list){
this.number = number;
this.infoList = list;
}
}
class Info{
public int id;
public String info;
}
I changed the PUT interface to this:
#PUT("device/activate.php")
Call<DeviceRegistry> registryDevice(#Body DataInfo info);
But I am getting the same problem.
Update 2: Do I need Header
I have this header in my REstfull client:
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Do I need to put this on my request configuration? How do I do that if I need it?
Update 3: checking the request type of my sending post.
Now I am checking the type of the request. Because I am having the same problem with the PUT/POST requests. So If can solved the problem with the put maybe all the problems will be solved.
When I execute the request and asking and inspect the request it is sending the the type (PUT/POST) but in the server php only detect or GET?? (the below example is using POST and the behavior is the same)
Call<UpdateResponse> requestCall = client.updateMedia(downloadItemList);
Log.i("CCC", requestCall .request().toString());
And the output is a POST:
Request{method=POST, url=http://myserver/api/v1/media/updateMedia.php, tag=null}
so I am sending a POST (no matter if I send a PUT) request to the sever but why in the server I am receiving a GET. I am locked!!! I don't know where is the problem.
Update 4: godaddy hosting.
I have my php server hosting on godaddy. Is there any problem with that? I create a local host and everything works pretty good but the same code is not working on godaddy. I did some research but I didn't find any good answer to this problem so Is possible that godaddy hosting is the problem?
PHP doesn't recognize anything other than GET and POST. the server should throw at you some kind of error like empty request.
To access PUT and other requests use
$putfp = fopen('php://input', 'r'); //will be a JSON string (provided everything got sent)
$putdata = '';
while($data = fread($putfp, filesize('php://input')))
$putdata .= $data;
fclose($putfp);
//php-like variable, if you want
$_PUT = json_decode($putdata);
did not tested, but should work.
I guess the problem is that you don't pass any data along with PUT request, that's why PHP recognizes the request as a GET. So I think you just need to try to pass some data using #FormUrlEncoded, #Multipart or probably #Body annotations
To add header in your retrofit2 you should create an interceptor:
Interceptor interceptor = new Interceptor() {
#Override
public okhttp3.Response intercept(Interceptor.Chain chain) throws IOException
{
okhttp3.Request.Builder ongoing = chain.request().newBuilder();
ongoing.addHeader("Content-Type", "application/x-www-form-urlencoded");
ongoing.addHeader("Accept", "application/json");
return chain.proceed(ongoing.build());
}
};
and add it to your client builder:
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.interceptors().add(interceptor);
PHP recognises 'PUT' calls. Extracted from PHP.net:
'REQUEST_METHOD' Which request method was used to access the page;
i.e. 'GET', 'HEAD', 'POST', 'PUT'.
You don't need to send any header if your server isn't expecting any
header.
Prior to use Retrofit or any other networking library, you should check the endpoint using a request http builder, like Postman or Advanced Rest Client. To debug the request/response when running your app or unit tests use a proxy like Charles, it will help you a lot to watch how your request/response really looks.
Hello Im having trouble with a service Im building. Right now I can see the body of the response in json and everything. But a friend who is getting the response on angular using $http.get('url'); seems to be having a hard time getting the data. Im not familiar with angular, so Im not sure how that call works and Im not sure why he cant get the data Im sending him. Here is the code:
$jsonResponse = array(
'success' => 'Llamada exitosa.',
'producto' => $responseproducts
);
$this->response->type('json');
$this->set('data', $jsonResponse);
$this->set('_serialize', 'data');
$json = json_encode($jsonResponse);
$this->response->body($json);
The $responseproducts is the array with the response I want him to get. The URL does return the array on the body when I try it on postman. And I can use the data on a ajax I build on a simple html to see if I could use the url. But for some reason my friend cant get the array on the data of the response. Any ideas what I might be doing wrong?
Hehe I found the answer. Digging a little bit more, it seems he had a problem with CORS availability. I just use the next code I found to let CORS on my service:
//Enabling CORS for CakePHP
public function beforeFilter() {
parent::beforeFilter();
$this->response->header('Access-Control-Allow-Origin','*');
$this->response->header('Access-Control-Allow-Methods','*');
$this->response->header('Access-Control-Allow-Headers','X-Requested-With');
$this->response->header('Access-Control-Allow-Headers','Content-Type, x-xsrf-token');
$this->response->header('Access-Control-Max-Age','172800');
}
As I read on the page where I found this code, not all 5 are necessary. Most of the time it works only with the first one, but test for the rest if necessary. Hope this helps soemone :P