Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the proper way to serve JSONP with CakePHP?

Tags:

jsonp

cakephp

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.

like image 247
Raisen Avatar asked Feb 02 '13 00:02

Raisen


3 Answers

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.

like image 121
Raisen Avatar answered Oct 04 '22 20:10

Raisen


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:

  1. 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)
  2. 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):

  1. 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);
});
like image 21
dKen Avatar answered Oct 04 '22 18:10

dKen


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);
    }
like image 40
Kim Stacks Avatar answered Oct 04 '22 19:10

Kim Stacks