hrsetyono
hrsetyono

Reputation: 4464

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

Upvotes: 8

Views: 8860

Answers (7)

thanassis
thanassis

Reputation: 691

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').

Upvotes: 0

Toan Nguyen Thai
Toan Nguyen Thai

Reputation: 71

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')
            ));
        }
    }

Upvotes: 1

masakielastic
masakielastic

Reputation: 4620

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);
    }
}

Upvotes: 1

Reactgular
Reactgular

Reputation: 54761

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.

Upvotes: 8

Schlaefer
Schlaefer

Reputation: 41

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();

Upvotes: 4

Sudhir Bastakoti
Sudhir Bastakoti

Reputation: 100175

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

Upvotes: 11

Dave
Dave

Reputation: 29121

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

Upvotes: 3

Related Questions