Why am I unable to access the json data POST'd to my codeigniter application? - json

I'm working on a RESTful application using CodeIgniter and I'm unable to access my POST'd json data in my controller.
I'm posting the json via cURL on my local machine, while the app is being developed on a remote server.
Here is the controller code in question:
class Products extends CI_Controller
{
public function __construct()
{
$this->load->model(products_model);
}
public function index($id = FALSE)
{
if($_SERVER['REQUEST_METHOD'] == 'GET')
{
// fetch product data
$product_data = $this->products_model->get_products($id)
// set appropriate header, output json
$this->output
->set_content_type(application/json)
->set_output(json_encode($product_data));
}
elseif($_SERVER['REQUEST_METHOD'] == 'POST')
{
// debugging for now, just dump the post data
var_dump($this->input->post());
}
}
}
The GET action is working well enough, and returning the appropriate data when requested from a browser, or via a cURL request. However, when attempting to POST json data via cURL I consistently get bool(FALSE) returned from the POST section of the index function. Here is the cURL request I'm making:
curl -X POST -d #product.json mydomain.com/restfulservice/products
Also, here is the contents of the product.json file:
{"id":"240",
"name":"4 x 6 Print",
"cost":"1.5900",
"minResolution":401,
"unitOfMeasure":"in",
"dimX":0,
"dimY":0,
"height":4,
"width":6}
I've made another POST via cURL, excluding the json data and passing something like this:
curl -X POST -d '&this=that' mydomain.com/restfulservice/products
Which returns
array(1) {
["this"]=>
string(4) "that"
}
What gives? Something with the json? It's valid. I've turned off the global CSRF and XSS in application/config/config.php as I understand they require use of CI's form_open() and won't work properly without it. It's my understanding that excluding parameters from $this->input->post() will return ALL the post items yet I continue to get none. I've also tried going around CI's input library and accessing the data via PHP's $_POST variable, it has made no difference.

Your post data is not in query string format so you should skip dealing with $_POST and go straight to the raw post data.
try
var_dump($HTTP_RAW_POST_DATA);
or even better
var_dump(file_get_contents("php://input"));

in codeigniter 2.X, you can override Input class and add necessary functionality.
https://ellislab.com/codeigniter/user-guide/general/core_classes.html
add file MY_Input.php to application/core
add code inside this file:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class MY_Input extends CI_Input {
public function raw_post() {
return file_get_contents('php://input');
}
public function post($index = NULL, $xss_clean = FALSE) {
$content_type = $this->get_request_header('Content-type');
if (stripos($content_type, 'application/json') !== FALSE
&& ($postdata = $this->raw_post())
&& in_array($postdata[0], array('{', '['))) {
$decoded_postdata = json_decode($postdata, true);
if ((json_last_error() == JSON_ERROR_NONE))
$_POST = $decoded_postdata;
}
return parent::post($index, $xss_clean);
}
}
that's it..
use it like normal post..

Related

Yii2 Functional test send ajax get request not working

I am trying to test an ajax request with a basic install of Yii2. I am just using the SiteController::actionAbout() method to try this out.
public function actionAbout()
{
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
return [
'message' => 'hello world',
'code' => 100,
];
}
And in the test:
public function sendAjax(\FunctionalTester $I)
{
$I->sendAjaxGetRequest('site/about');
$r = $I->grabResponse();
die(var_dump($r));
}
The grab response method is one I wrote myself in a \Helper\Functional.php file:
public function grabResponse()
{
return $this->getModule('Yii2')->_getResponseContent();
}
When I dump out the response, I just see the html from the site/index page.
This happens for any route except the site default. If I try to do the same thing using site/index then it returns:
string(36) "{"message":"hello world","code":100}"
What am I missing / doing wrong?
Note the difference between a Yii route and the actual URL.
The argument to sendAjaxGetRequest() is the URL, not the route (at least if you pass it as a string, see below).
Dependent on your UrlManager config you might have URLs like /index.php?r=site/about, where the route is site/about. You can use the Url helper of Yii to create the URL:
$I->sendAjaxGetRequest(\yii\helpers\Url::to(['site/about']));
I am not sure about this but if you have the Codeception Yii2 module installed you should also be able to pass the route like this:
$I->sendAjaxGetRequest(['site/about']);

Connect Parse with external database?

I am working on project where it is build using traditional technologies like PHP, mysql and it is a web application.
Now we want to build an app for mobile users on platform like Andoid, iOS.
So we are thinking to connect MySql with Parse.com database.
I know parse uses NoSql kind of database for storing objects.
So my question is can we connect parse database to any other SQL database ?
If yes then how we can do that ?
EDIT
#Luca laco I just created a new cloud function like you. which is below.
Parse.Cloud.define("get_parse4j_object",
function(request,response){
// Parameters from client (iOS/Android app)
//var requestedObjectId = request.params.objectId;
// Calling beckend service for getting user information
Parse.Cloud.httpRequest({
method: "GET",
headers: {
'Content-Type': 'application/json'
},
url: "https://api.parse.com/1/parse4j/MLiOgxncUM", /* This could be your url for the proper php module */
//body: { "objectId":requestedObjectId }, /* Here you compose the body request for the http call, passing the php parameters and their values */
success: function(httpResponse) {
/* We expect that the php response is a Json string, using the header('application/json'), so: */
var jsonResponse = JSON.parse(httpResponse.text);
/* sample structure in jsonResponse: { "name":"Joe", "surname":"Banana", "birth_date":"01-02-1999" } */
/* Do any additional stuff you need... */
/* return the result to your iOS/Android client */
return response.success( { "myRequestedUserInfo" : jsonResponse } );
alert(jsonResponse);
},
error: function(httpResponse) {
return response.error({ "msg":"Unable to fetch this user", "code":123456 }); // sample error response
}
});
});
I followed the same way which Luca Laco explained me.
But I am getting error when I am calling function from client JS.
This is my client JS
<script type="text/javascript">
Parse.initialize("APP_ID", "JAVASCRIPT_KEY");
Parse.Cloud.run('get_parse4j_object', {}, {
success: function(result) {
alert(result);
},
error: function(error) {
alert(JSON.stringify(error));
}
});
</script>
In the network tab I can see
POST https://api.parse.com/1/functions/get_parse4j_object 400 (Bad Request)
and error is: {"code":141, "message":"function not found"}
Where I am missing and doing wrong ?
If you mean something like a common mysql connector, then the response is no, you can't. At now, The only way to make parse and something else in relation, is to query from and to Parse. To be clear:
If you want to get a value from Parse, that is stored in mysql, you have to use a http request to a specific php module stored on your php website ( and implemented by you ) that expect some paramenter, and return the result in a specific way, normally in json format, using also the http header application/json.
If you want to get a value from php, that is stored on the parse db, you can run a REST call from php following the spec on the parse website ( https://parse.com/docs/rest/guide/ ), or simply using the php sdk ( https://github.com/ParsePlatform/parse-php-sdk ). Take a look also to the Parse Webhooks.
From what i understood, you already have a working web service, so doing this, you would just proxy the resources stored on your server on mysql to your clients through Parse. In other words you should create a Parse Cloud function for each type of information you want to retrieve on the clients using the Parse SDK (for iOS or Android) and another Parse Colud function for each action you perform on your devices and you want to save on your mysql db, always through Parse system.
My personal opinion, is to stay on Mysql, especially because on Parse we still have a lot of limitation on the queries ( no group by, no distinct, query timeout, etc. ), while seems to be a really good service for the push notification. Anyway all this depends by the complexity of your software and as i said, is just my opinion.
[Edit]
Here an example:
In Parse cloud code, let's make a cloud function called 'get_user_info'
Parse.Cloud.define("get_user_info",
function(request,response){
// Parameters from client (iOS/Android app)
var requestedUserId = request.params.user_id;
// Calling beckend service for getting user information
Parse.Cloud.httpRequest({
method: "POST",
url: "https://www.yourPhpWebsite.com/getUser.php", /* This could be your url for the proper php module */
body: { "php_param_user_id":requestedUserId }, /* Here you compose the body request for the http call, passing the php parameters and their values */
success: function(httpResponse) {
/* We expect that the php response is a Json string, using the header('application/json'), so: */
var jsonResponse = JSON.parse(httpResponse.text);
/* sample structure in jsonResponse: { "name":"Joe", "surname":"Banana", "birth_date":"01-02-1999" } */
/* Do any additional stuff you need... */
/* return the result to your iOS/Android client */
return response.success( { "myRequestedUserInfo" : jsonResponse } );
},
error: function(httpResponse) {
return response.error({ "msg":"Unable to fetch this user", "code":123456 }); // sample error response
}
});
});
The sample 'getUser.php' module could be
<?php
$expectedUserId = $_POST['php_param_user_id'];
// query your MySql db using passed user id
$query = "SELECT name,surname,birth_date FROM MyUserTable Where id = ".$expectedUserId;
// perform your query (the above one is just an example, would be better to use PDO and any other check, just to avoid SQL Injection)
// ...
// ..
// .
$resultQuery = row[0];
// sample json structure
$jsonResponseToParse = '{ "name":'.resultQuery["name"].', "surname":'.resultQuery["surname"].', "birth_date":'.resultQuery["birth_date"].' }';
header('application/json');
echo jsonResponseToParse;
exit();
?>
Hope it helps

How to use update function to upload attachment in CouchDB

I would like to know what can I do to upload attachments in CouchDB using the update function.
here you will find an example of my update function to add documents:
function(doc, req){
if (!doc) {
if (!req.form._id) {
req.form._id = req.uuid;
}
req.form['|edited_by'] = req.userCtx.name
req.form['|edited_on'] = new Date();
return [req.form, JSON.stringify(req.form)];
}
else {
return [null, "Use POST to add a document."]
}
}
example for remove documents:
function(doc, req){
if (doc) {
for (var i in req.form) {
doc[i] = req.form[i];
}
doc['|edited_by'] = req.userCtx.name
doc['|edited_on'] = new Date();
doc._deleted = true;
return [doc, JSON.stringify(doc)];
}
else {
return [null, "Document does not exist."]
}
}
thanks for your help,
It is possible to add attachments to a document using an update function by modifying the document's _attachments property. Here's an example of an update function which will add an attachment to an existing document:
function (doc, req) {
// skipping the create document case for simplicity
if (!doc) {
return [null, "update only"];
}
// ensure that the required form parameters are present
if (!req.form || !req.form.name || !req.form.data) {
return [null, "missing required post fields"];
}
// if there isn't an _attachments property on the doc already, create one
if (!doc._attachments) {
doc._attachments = {};
}
// create the attachment using the form data POSTed by the client
doc._attachments[req.form.name] = {
content_type: req.form.content_type || 'application/octet-stream',
data: req.form.data
};
return [doc, "saved attachment"];
}
For each attachment, you need a name, a content type, and body data encoded as base64. The example function above requires that the client sends an HTTP POST in application/x-www-form-urlencoded format with at least two parameters: name and data (a content_type parameter will be used if provided):
name=logo.png&content_type=image/png&data=iVBORw0KGgoA...
To test the update function:
Find a small image and base64 encode it:
$ base64 logo.png | sed 's/+/%2b/g' > post.txt
The sed script encodes + characters so they don't get converted to spaces.
Edit post.txt and add name=logo.png&content_type=image/png&data= to the top of the document.
Create a new document in CouchDB using Futon.
Use curl to call the update function with the post.txt file as the body, substituting in the ID of the document you just created.
curl -X POST -d #post.txt http://127.0.0.1:5984/mydb/_design/myddoc/_update/upload/193ecff8618678f96d83770cea002910
This was tested on CouchDB 1.6.1 running on OSX.
Update: #janl was kind enough to provide some details on why this answer can lead to performance and scaling issues. Uploading attachments via an upload handler has two main problems:
The upload handlers are written in JavaScript, so the CouchDB server may have to fork() a couchjs process to handle the upload. Even if a couchjs process is already running, the server has to stream the entire HTTP request to the external process over stdin. For large attachments, the transfer of the request can take significant time and system resources. For each concurrent request to an update function like this, CouchDB will have to fork a new couchjs process. Since the process runtime will be rather long because of what is explained next, you can easily run out of RAM, CPU or the ability to handle more concurrent requests.
After the _attachments property is populated by the upload handler and streamed back to the CouchDB server (!), the server must parse the response JSON, decode the base64-encoded attachment body, and write the binary body to disk. The standard method of adding an attachment to a document -- PUT /db/docid/attachmentname -- streams the binary request body directly to disk and does not require the two processing steps.
The function above will work, but there are non-trivial issues to consider before using it in a highly-scalable system.

Cakephp - If Request is JSON?

I have read the RequestHandler part in cookbook. There are isXml(), isRss(), etc. But there's no isJson().
Any other way to check whether a request is JSON?
So when the url is mysite.com/products/view/1.json it will give JSON data, but without .json it will give the HTML View.
Thanks
I dont think cakePHP has some function like isJson() for json data, you could create your custom though, like:
//may be in your app controller
function isJson($data) {
return (json_decode($data) != NULL) ? true : false;
}
//and you can use it in your controller
if( $this->isJson($your_request_data) ) {
...
}
Added:
if you want to check .json extension and process accordingly, then you could do in your controller:
$this->request->params['ext']; //which would give you 'json' if you have .json extension
CakePHP is handling this correctly, because JSON is a response type and not a type of request. The terms request and response might be causing some confusing. The request object represents the header information of the HTTP request sent to the server. A browser usually sends POST or GET requests to a server, and those requests can not be formatted as JSON. So it's not possible for a request to be of type JSON.
With that said, the server can give a response of JSON and a browser can put in the request header that it supports a JSON response. So rather than check what the request was. Check what accepted responses are supported by the browser.
So instead of writing $this->request->isJson() you should write $this->request->accepts('application/json').
This information is ambiguously shown in the document here, but there is no reference see also links in the is(..) documentation. So many people look there first. Don't see JSON and assume something is missing.
If you want to use a request detector to check if the browser supports a JSON response, then you can easily add a one liner in your beforeFilter.
$this->request->addDetector('json',array('callback'=>function($req){return $req->accepts('application/json');}));
There is a risk associated with this approach, because a browser can send multiple response types as a possible response from the server. Including a wildcard for all types. So this limits you to only requests that indicate a JSON response is supported. Since JSON is a text format a type of text/plain is a valid response type for a browser expecting JSON.
We could modify our rule to include text/plain for JSON responses like this.
$this->request->addDetector('json',array('callback'=>function($req){
return $req->accepts('application/json') || $req->accepts('text/plain');
}));
That would include text/plain requests as a JSON response type, but now we have a problem. Just because the browser supports a text/plain response doesn't mean it's expecting a JSON response.
This is why it's better to incorporate a naming convention into your URL to indicate a JSON response. You can use a .json file extension or a /json/controller/action prefix.
I prefer to use a named prefix for URLs. That allows you to create json_action methods in your controller. You can then create a detector for the prefix like this.
$this->request->addDetector('json',array('callback'=>function($req){return isset($req->params['prefix']) && $req->params['prefix'] == 'json';}));
Now that detector will always work correctly, but I argue it's an incorrect usage of detecting a JSON request. Since there is no such thing as a JSON request. Only JSON responses.
You can make your own detectors. See: http://book.cakephp.org/2.0/en/controllers/request-response.html#inspecting-the-request
For example in your AppController.php
public function beforeFilter() {
$this->request->addDetector(
'json',
[
'callback' => [$this, 'isJson']
]
);
parent::beforeFilter();
}
public function isJson() {
return $this->response->type() === 'application/json';
}
Now you can use it:
$this->request->is('json'); // or
$this->request->isJson();
Have you looked through and followed the very detailed instructions in the book?:
http://book.cakephp.org/2.0/en/views/json-and-xml-views.html
class TestController extends Controller {
public $autoRender = false;
public function beforeFilter() {
$this->request->addDetector('json', array('env' => 'CONTENT_TYPE', 'pattern' => '/application\/json/i'));
parent::beforeFilter();
}
public function index() {
App::uses('HttpSocket', 'Network/Http');
$url = 'http://localhost/myapp/test/json';
$json = json_encode(
array('foo' => 'bar'),
JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
);
$options = array('header' => array('Content-Type' => 'application/json'));
$request = new HttpSocket();
$body = $request->post($url, $json, $options)->body;
$this->response->body($body);
}
public function json() {
if ($this->request->isJson()) {
$data = $this->request->input('json_decode');
$value = property_exists($data, 'foo') ? $data->foo : '';
}
$body = (isset($value) && $value === 'bar') ? 'ok' : 'fail';
$this->response->body($body);
}
}
Thanks a lot Mr #Schlaefer. I read your comment and try, Wow it's working now.
//AppController.php
function beforeFilter() {
$this->request->addDetector(
'json', [
'callback' => [$this, 'isJson']
]
);
parent::beforeFilter();
...
}
public function isJson() {
return $this->response->type() === 'application/json';
}
//TasksController.php
public $components = array('Paginator', 'Flash', Session','RequestHandler');
//Get tasks function return all tasks in json format
public function getTasks() {
$limit = 20;
$conditions = array();
if (!empty($this->request->query['status'])) {
$conditions = ['Task.status' => $this->request->query['status']];
}
if (!empty($this->request->query['limit'])) {
$limit = $this->request->query['limit'];
}
$this->Paginator->settings = array('limit' => $limit, 'conditions' => $conditions);
$tasks = $this->paginate();
if ($this->request->isJson()) {
$this->set(
array(
'tasks' => $tasks,
'_serialize' => array('tasks')
));
}
}
In case anybody is reading this in the days of CakePHP 4, the correct and easy way to do this is by using $this->request->is('json').

Grails send request as JSON and parse it in controller

I want to send a request as JSON and in my controller I want to parse this JSON and get the parameters I want. for example this is the request:
{"param1":"val1"}
I want to parse this request and get "param1" value. I used request.JSON but still I got null. Is there any other way to solve this?
Thanks,
You can use one of the following to test your stuff (both options could be re-used as automated tests eventually - unit and integration):
write a unit test for you controller like (no need to start the server):
void testConsume() {
request.json = '{param1: "val1"}'
controller.consume()
assert response.text == "val1"
}
and let's say your controller.consume() does something like:
def consume() {
render request.JSON.param1
}
Or you can use for example the Jersey Client to do a call against your controller, deployed this time:
public void testRequest() {
// init the client
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
// create a resource
WebResource service = client.resource(UriBuilder.fromUri("your request url").build());
// set content type and do a POST, which will accept a text/plain response as well
service.type(MediaType.APPLICATION_JSON).accept(MediaType.TEXT_PLAIN).put(Foo.class, foo);
}
, where foo is a Foo like this:
#XmlRootElement
public class Foo {
#XmlElement(name = "param1")
String param1;
public Foo(String val){param1 = val;}
}
Here are some more examples on how to use the Jersey client for various REST requests:
https://github.com/tavibolog/TodaySoftMag/blob/master/src/test/java/com/todaysoftmag/examples/rest/BookServiceTest.java
Set it in your UrlMappings like this:
static mappings = {
"/rest/myAction" (controller: "myController", action: "myAction", parseRequest: true)
}
Search for parseRequest in latest Grails guide.
Then validate if it works correctly with curl:
curl --data '{"param1":"value1"}' --header "Content-Type: application/json" http://yourhost:8080/rest/myAction
In the controller method, check request.format. It should specify json. I'm guessing it won't here, but it may give you clues as to how your payload is being interpreted.
In your Config.groovy file, I would set the following values:
grails.mime.file.extensions = false
grails.mime.use.accept.header = false
In that same file, check your grails.mime.types. make sure it includes json: ['application/json', 'text/json'], which it probably will, but put it above */*. These entries are evaluated in order (this was true in pre 2.1 versions, havent' verified it is now, but what the heck). In conjunction with that, as aiolos mentioned, set your content-type header to one of the above mime-types.
Finally, test with curl, per Tomasz KalkosiƄski, or, to use RESTClient for FF, click on "Headers" in the very top of the client page (there are 4 clickable items at the top-left; headers is one. From a fresh RESTClient, you may have to choose "Custom Header". I can't recall)