I am implementing Backbone.js with Codeigniter, and having a hard time receiving a proper response from Codeigniter upon a Ajax call. I was doing a #Create, which led to #save, and then #set, right at there, it breaks and couldn't find the ID in the format I returned the data.
For testing purpose, I'm echo-ing
'[{"id":"100"}]'
right back to the browswer, still it couldn't find it.
Anyone aware of a Backbone/Codeigniter(or similar) RESTful implementation example?
You need to return 200 response code or it would not pass as good response.
I built few apps with Backbone/CI combo and it is much easier if you use Phil Sturgeon's REST implementation for CodeIgniter
Than you controller that is located at url example.com/api/user and directory applications/controllers/api/user.php would look something like this:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
include APPPATH.'core/REST_Controller.php'; // MUST HAVE THIS LINE!!!
class User extends REST_Controller {
// update user
public function index_put() // prefix http verbs with index_
{
$this->load->model('Administration');
if($this->Administration->update_user($this->request->body)){ // MUST USE request->body
$this->response(NULL, 200); // this is how you return response with success code
return;
}
$this->response(NULL, 400); // this is how you return response with error code
}
// create user
public function index_post()
{
$this->load->model('Administration');
$new_id = $this->Administration->add_user($this->request->body);
if($new_id){
$this->response(array('id' => $new_id), 200); // return json to client (you must set json to default response format in app/config/rest.php
return;
}
$this->response(NULL, 400);
}
// deleting user
public function index_delete($id)
{
$this->load->model('Administration');
if($this->Administration->delete_user($id)){
$this->response(NULL, 200);
return;
}
$this->response(NULL, 400);
}
}
It will help you to return proper responses.
TIP: Also whatever you return to client will be set to model attributes. E.g. when creating user if you return only:
'[{"id":"100"}]'
model will be assigned id 100. But if you return:
'[{"id":"100", "date_created":"20-aug-2011", "created_by": "Admin", "random": "lfsdlkfskl"}]'
all this key value pairs will be set to user model (I added this just for clarity, since it confused me in start)
IMPORTANT: this is for CI 2.0+ if you are using 1.7.x REST implementation is a tiny bit different that concerns directory structure
Related
I'm trying to integrate PayPal's IPN code into CakePHP 3.
namespace App\Controller;
use PayPal\Api\PaypalIPN;
class IpnController extends AppController
{
public function index()
{
$this->autoRender = false;
$ipn = new PayPalIPN();
// Use the sandbox endpoint during testing.
$ipn->useSandbox();
$verified = $ipn->verifyIPN();
if ($verified) {
/*
* Process IPN
* A list of variables is available here:
* https://developer.paypal.com/webapps/developer/docs/classic/ipn/integration-guide/IPNandPDTVariables/
*/
}
// Reply with an empty 200 response to indicate to paypal the IPN was received correctly.
header("HTTP/1.1 200 OK");
}
}
This is failing to validate on PayPal's end and I'm suspecting it has to do with setting the headers in the controller view.
Is there a way to set the header properly in CakePHP's controller.
I had this code running stand alone (in just a php file) and it seemed to work just fine.
You should not output any data in your controller action - that means you should not use echo, header() or any function or construct that would return anything to browser. If you do, you will encounter a "headers already sent" error.
If you want to set headers, you should use withHeader() or withAddedHeader() methods of Cake\Http\Response.
For status codes, you also have withStatus() method:
$response = $this->response;
$response = $response->withStatus(200,"OK");
return $response; // returning response will stop controller from rendering a view.
More about setting headers can be found in docs:
Setting response headers in CakePHP 3
Cake\Http\Response::withStatus()
Maybe that's not very Cakish, but actually one can send headers this way - it just have to be followed by die; or exit; to prevent app from further response processing.
Anyway, for sure your problem is not associated with headers. IPN seems to doesn't work properly with Paypal Sandbox. Maybe you should try it other way with ApiContext class?
Hi I'm trying to update my database with function that returns a number
$scope.sum = function()
{
return $scope.inp + $scope.points;
};
this function will update the record in object points, column name and id 1:
$scope.addPoint = function() {
PointService.addPoint($scope.sum, 1)
.then(function(result) {
$scope.inp = 0;
getMyPoints();
});
}
addPoint = function(id,points)
{
return $http.put(getUrlForId(1),points,name);
}
the error is: Error details: Cannot convert type 'int' to 'System.Collections.Generic.Dictionary'
the data type of the field is Float.
Any idea what is wrong with the code?
you are passing function reference to PointService.addPointer(),
use this:
$scope.addPoint = function() {
PointService.addPoint($scope.sum(), 1) // NOT PointService.addPoint($scope.sum, 1)
.then(function(result) {
$scope.inp = 0;
getMyPoints();
});
}
this will execute your function and pass the output (id parameter) to addPoint function, further, for more safe side, you can return Number from $scope.sum() i.e.
$scope.sum = function()
{
return Number($scope.inp + $scope.points);
};
This looks like an issue with how you're contacting Backand. You use the following code to send your points over:
addPoint = function(id,points)
{
return $http.put(getUrlForId(1),points,name);
}
This is an older version of calling the Backand API that is manually constructing a PUT request, and putting "points" and "name" as the "Data" and "config" parameters to $http. With an object update via PUT, you'll need to provide the updates as an object. So if you wanted to update the points and the name of the object (and I'm doing some assuming based upon what I can tell from the code snippet above), you'd need to encapsulate these properties in an object that has the following general format:
{
"field_name_1":"new value_1",
"field_name_2":"new value_2",
etc ...
}
This should then be sent as the body of the request. So, for your code, change it to the following and see if this helps:
addPoint = function(id,points)
{
return $http.put(getUrlForId(1),{points: points, name: name});
}
To give more info on why you're seeing this particular error, Backand is depending on this JSON format in the body. Our platform should definitely do more validation (and I'll create a ticket for the devs to handle non-conforming input more gracefully), but at the moment we simply take the body of the request, convert it to a dictionary object, then begin the requested operation. As your code above sends only "1.0" as the body, this fails the conversion into a dictionary, causing the stack exception and the issue you are seeing.
As a note, we offer a new SDK that encapsulates these HTTP methods, performing the authentication header generation and HTTP messaging for you, providing promises to handle responses. You can find it on our Github page at https://github.com/backand/vanilla-sdk. To make the same call using the new SDK, the code would resemble the following:
backand.object.update("your object name", 1, {name: name, points: points})
.then(function(response){
console.log(response.data);
});
Currently, I'm using the CRUD v4 plugin for Cakephp 3. For the edit function in my user controller it is important that only a user itself can alter his or her credentials. I want to make this possible by inserting the user id from the authentication component. The following controller method:
public function edit($id = null){
$this->Crud->on('beforeSave', function(\Cake\Event\Event $event) {
$event->subject()->entity->id = $this->Auth->user('id');
});
return $this->Crud->execute();
}
How can I make sure I don't need to give the id through the url? The standard implementation requires the url give like this: http://domain.com/api/users/edit/1.json through PUT request. What I want to do is that a user can just fill in http://domain.com/api/users/edit.json and send a JSON body with it.
I already tried several things under which:
$id = null when the parameter is given, like in the example above. Without giving any id in the url this will throw a 404 error which is caused by the _notFound method in the FindMethodTrait.php
Use beforeFind instead of beforeSave. This doesn't work either since this isn't the appropriate method for the edit function.
Give just a random id which doesn't exist in the database. This will through a 404 error. I think this is the most significant sign (combined with point 1) that there is something wrong. Since I try to overwrite this value, the CRUD plugin doesn't allow me to do that in a way that my inserting value is just totally ignored (overwriting the $event->subject()->entity->id).
Try to access the method with PUT through http://domain.com/api/users.json. This will try to route the action to the index method.
Just a few checks: the controllerTrait is used in my AppController and the crud edit function is not disabled.
Does anyone know what I'm doing wrong here? Is this a bug?
I personally would use the controller authorize in the Auth component to prevent anyone from updating someone else's information. That way you do not have to change up the crud code. Something like this...
Add this line to config of the Auth component (which is probably in your AppController):
'authorize' => ['Controller']
Then, inside the app controller create a function called isAuthorized:
public function isAuthorized($user) {
return true;
}
Then, inside your UsersController you can override the isAuthorized function:
public function isAuthorized($user) {
// The owner of an article can edit and delete it
if (in_array($this->request->action, ['edit'])) {
$userId = (int)$this->request->params['pass'][0];
if ($user['id'] !== $userId) {
return false;
}
}
return parent::isAuthorized($user);
}
I want to serve JSONP content with CakePHP and was wondering what's the proper way of doing it so.
Currently I'm able to serve JSON content automatically by following this CakePHP guide.
Ok, I found a solution on this site. Basically you override the afterFilter method with:
public function afterFilter() {
parent::afterFilter();
if (empty($this->request->query['callback']) || $this->response->type() != 'application/json') {
return;
}
// jsonp response
App::uses('Sanitize', 'Utility');
$callbackFuncName = Sanitize::clean($this->request->query['callback']);
$out = $this->response->body();
$out = sprintf("%s(%s)", $callbackFuncName, $out);
$this->response->body($out);
}
I hope it helps someone else as well.
I've as yet not found a complete example of how to correctly return JSONP using CakePHP 2, so I'm going to write it down. OP asks for the correct way, but his answer doesn't use the native options available now in 2.4. For 2.4+, this is the correct method, straight from their documentation:
Set up your views to accept/use JSON (documentation):
Add Router::parseExtensions('json'); to your routes.php config file. This tells Cake to accept .json URI extensions
Add RequestHandler to the list of components in the controller you're going to be using
Cake gets smart here, and now offers you different views for normal requests and JSON/XML etc. requests, allowing you flexibility in how to return those results, if needed. You should now be able to access an action in your controller by:
using the URI /controller/action (which would use the view in /view/controller/action.ctp), OR
using the URI /controller/action.json (which would use the view in /view/controller/json/action.ctp)
If you don't want to define those views i.e. you don't need to do any further processing, and the response is ready to go, you can tell CakePHP to ignore the views and return the data immediately using _serialize. Using _serialize will tell Cake to format your response in the correct format (XML, JSON etc.), set the headers and return it as needed without you needing to do anything else (documentation). To take advantage of this magic:
Set the variables you want to return as you would a view variable i.e. $this->set('post', $post);
Tell Cake to serialize it into XML, JSON etc. by calling $this->set('_serialize', array('posts'));, where the parameter is the view variable you just set in the previous line
And that's it. All headers and responses will be taken over by Cake. This just leaves the JSONP to get working (documentation):
Tell Cake to consider the request a JSONP request by setting $this->set('_jsonp', true);, and Cake will go find the callback function name parameter, and format the response to work with that callback function name. Literally, setting that one parameter does all the work for you.
So, assuming you've set up Cake to accept .json requests, this is what your typical action could look like to work with JSONP:
public function getTheFirstPost()
$post = $this->Post->find('first');
$this->set(array(
'post' => $post, <-- Set the post in the view
'_serialize' => array('post'), <-- Tell cake to use that post
'_jsonp' => true <-- And wrap it in the callback function
)
);
And the JS:
$.ajax({
url: "/controller/get-the-first-post.json",
context: document.body,
dataType: 'jsonp'
}).done(function (data) {
console.log(data);
});
For CakePHP 2.4 and above, you can do this instead.
http://book.cakephp.org/2.0/en/views/json-and-xml-views.html#jsonp-response
So you can simply write:
$this->set('_jsonp', true);
in the relevant action.
Or you can simply write:
/**
*
* beforeRender method
*
* #return void
*/
public function beforeRender() {
parent::beforeRender();
$this->set('_jsonp', true);
}
I am a new bie on GWT, I wrote an application on abc.com, I have another application i.e. xyz.com, xyz.com?id=1 provides me a data in json format, I was thinking to find a way that how to get that json file in abc.com via RPC call, because I have seen tutorials in which RPC calls are used to get data from its server. any help will be appreciated.
EDIT
I am trying to implement this in this StockWatcher tutorial
I changed my code slightly change to this
private static final String JSON_URL = "http://localhost/stockPrices.php?q=";
AND
private void refreshWatchList() {
if (stocks.size() == 0) {
return;
}
String url = JSON_URL;
// Append watch list stock symbols to query URL.
Iterator iter = stocks.iterator();
while (iter.hasNext()) {
url += iter.next();
if (iter.hasNext()) {
url += "+";
}
}
url = URL.encode(url);
MyJSONUtility.makeJSONRequest(url, new JSONHandler() {
#Override
public void handleJSON(JavaScriptObject obj) {
if (obj == null) {
displayError("Couldn't retrieve JSON");
return;
}
updateTable(asArrayOfStockData(obj));
}
});
}
before when I was requesting my url via RequestBuilder it was giving me an exception Couldn't retrieve JSON but now JSON is fetched and status code is 200 as I saw that in firebug but it is not updating on table. Kindly help me regarding this.
First, you need to understand the Same Origin Policy which explains how browsers implement a security model where JavaScript code running on a web page may not interact with any resource not originating from the same web site.
While GWT's HTTP client and RPC call can only fetch data from the same site where your application was loaded, you can get data from another server if it returns json in the right format. You must be interacting with a JSON service that can invoke user defined callback functions with the JSON data as argument.
Second, see How to Fetch JSON DATA