Ajax error handling in CakePHP

I want to do something very similar to this , but in the CakePHP world for AJAX requests. At the moment I am doing this:

$this->autoRender = false; $this->response->statusCode(500); 

It is based on this . However, this solution does not allow me to include a custom message, as in the Rails example, so in this way, in the client-side error handler, I can display the message included in the 500 error response.

How to implement the same functionality in CakePHP, like the Ruby on Rails example?

+8
ajax php error-handling cakephp
source share
3 answers

You can use CakeExceptions as described in the Cookbook: http://book.cakephp.org/2.0/en/development/exceptions.html BUT , if you want to use your own messages I have not found any other way than using debug = 1 in your working mode: (

Here is my approach with built-in methods:

In your controller:

 if($this->request->is('ajax')){ Configure::write('debug', 1); } if(!$allowed) { throw new InternalErrorException('Keep your fingers away from me!'); // 500 error } 

Change the error pattern to display nothing but an error when used in AJAX calls in /app/View/Errors/error500.ctp:

 <?php if($this->request->is('ajax')): // Output for AJAX calls echo $name; else: //Standard CakePHP output ?> <h2><?php echo $name; ?></h2> <p class="error"> <strong><?php echo __d('cake', 'Error'); ?>: </strong> <?php echo __d('cake', 'An Internal Error Has Occurred.'); ?> </p> <?php if (Configure::read('debug') > 0 ): echo $this->element('exception_stack_trace'); endif; endif; ?> 

Then you can parse the returned text in AJAX. The jQuery elements that I use are used here:

 //... error: function (request) { yourErrorShowingFunction(_this, request.responseText); } //... 

Hope this helps :)

If anyone has an idea how to use custom errors in production mode (without overwriting debug mode), I would be very happy!

0
source share

As mentioned above, Exceptions are a way to return an error in an AJAX request in CakePHP. Here is my solution for getting more precise control over how the error looks. Also, as above, I am using a custom Exception Renderer, but not a custom exception. The default error response is a JSON object similar to this:

 {"name":"An Internal Error Has Occurred", "url": "\/users\/login.json"} 

I almost like how the default renderer handles AJAX errors; I just want to tweak it a bit:

 <?php // File: /app/Lib/Error/CustomExceptionRenderer.php App::uses('ExceptionRenderer', 'Error'); class CustomExceptionRenderer extends ExceptionRenderer { // override public function error400($error) { $this->_prepareView($error, 'Not Found'); $this->controller->response->statusCode($error->getCode()); $this->_outputMessage('error400'); } // override public function error500($error) { $this->_prepareView($error, 'An Internal Error Has Ocurred.'); $code = ($error->getCode() > 500 && $error->getCode() < 506) ? $error->getCode() : 500; $this->controller->response->statusCode($code); $this->_outputMessage('error500'); } private function _prepareView($error, $genericMessage) { $message = $error->getMessage(); if(!Configure::read('debug') && !Configure::read('detailed_exceptions')) { $message = __d('cake', $genericMessage); } $url = $this->controller->request->here(); $renderVars = array( 'name' => h($message), 'url' => h($url), ); if(isset($this->controller->viewVars['csrf_token'])) { $renderVars['csrf_token'] = $this->controller->viewVars['csrf_token']; } $renderVars['_serialize'] = array_keys($renderVars); $this->controller->set($renderVars); } } 

Then in bootstrap.php:

 Configure::write('Exception.renderer', 'CustomExceptionRenderer'); 

So here is how it works:

  • Suppose I want to return a new CSRF token to my error response, so if my existing token expires before the exception is thrown, I do not get blackholed the next time I try the request. See the Security Component Documentation for more information on CSRF protection.
  • Create a new class in the application / Lib / Error. You can expand the default rendering or not. Since I just want to change a few small things, and to make a simple example, I am expanding it.
  • Override the methods that the default renderer uses to create the JSON object that will be returned. This is done using the request handler component and is in line with best practices. Indeed, the default renderer does the same.
  • A new private method to keep things dry.
  • My solution to the problem of not getting custom error messages during the production process is to add an optional configuration key. By default, this class will display general messages during production, but if you have debug set to 0 and you need specific error messages: Configure::write('detailed_exceptions', 1);
  • Add a new token to the response, if one exists. In my case, I already called Controller::set in the new token in the beforeFilter method of the AppController application, so it is available in $this->controller->viewVars . There are probably dozens of other ways to achieve this.

Your answer now looks like this:

 { "name":"The request has been black-holed", "url":"\/users\/login.json", "csrf_token":"1279f22f9148b6ff30467abaa06d83491c38e940" } 

Any additional data of any type can be added to the array passed to Controller::set for the same result.

+7
source share

I also struggled with custom exceptions and error codes when using ajax requests (jquery mobile in my case). Here is the solution I came across without having to overwrite debug mode. It generates user errors in development mode, and also optionally in production mode. I hope this helps someone:

AppExceptionRenderer.php:

 <?php App::uses('ExceptionRenderer', 'Error'); class AppExceptionRenderer extends ExceptionRenderer { public function test($error) { $this->_sendAjaxError($error); } private function _sendAjaxError($error) { //only allow ajax requests and only send response if debug is on if ($this->controller->request->is('ajax') && Configure::read('debug') > 0) { $this->controller->response->statusCode(500); $response['errorCode'] = $error->getCode(); $response['errorMessage'] = $error->getMessage(); $this->controller->set(compact('response')); $this->controller->layout = false; $this->_outputMessage('errorjson'); } } } 

You can leave Configure::read('debug') > 0 if you want to display an exception in debug mode. The errorjson.ctp view is in 'Error / errorjson.ctp':

 <?php echo json_encode($response); ?> 

In this case, my exception is called

Testexception

and is defined as follows:

 <?php class TestException extends CakeException { protected $_messageTemplate = 'Seems that %s is missing.'; public function __construct($message = null, $code = 2) { if (empty($message)) { $message = 'My custom exception.'; } parent::__construct($message, $code); } } 

Where I have error code 2, $code = 2 , for my json answer. The ajax response will throw a 500 error with the following json data:

 {"errorCode":"2","errorMessage":"My custom exception."} 

Obviously, you also need to throw an exception from your controller:

 throw new TestException(); 

and enable the exception visualizer http://book.cakephp.org/2.0/en/development/exceptions.html#using-a-custom-renderer-with-exception-renderer-to-handle-application-exceptions

It might be a bit out of scope, but to handle the ajax error response in jQuery, I use:

 $(document).ajaxError(function (event, jqXHR, ajaxSettings, thrownError) { //deal with my json error }); 
+2
source share

All Articles