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 off of this. However this solution does not allow me to include a custom message like in the Rails example, so that way, in my clients side error handler, I can display the message included in the 500 error response.
How would I implement the same functionality in CakePHP like the Ruby on Rails example?
As mentioned above, Exceptions are the way to return an error on an AJAX request in CakePHP. Here is my solution for gaining finer control of what the error looks like. Also, as above, I am using a custom Exception Renderer, but not a custom exception. The default error response is a JSON object like this:
{"name":"An Internal Error Has Occurred", "url": "\/users\/login.json"}
I almost like the way that the default renderer handles AJAX errors; I just want to tweak it a little:
<?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:
Configure::write('detailed_exceptions', 1);
Controller::set
on the new token in the beforeFilter method of AppController, so it is available in $this->controller->viewVars
. There are probably dozens of other ways of accomplishing this.Now your response 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.
I have also struggled with custom exceptions and error codes when using ajax requests (jquery mobile in my case). Here is the solution I came up with, without involving overwriting the debug mode. It throws custom errors in development mode, and also optionally in production mode. I hope it 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 out Configure::read('debug') > 0
if you want to display the exception in debug mode. The view errorjson.ctp is located 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 a custom error code 2, $code = 2
, for my json response. The ajax response will cast an error 500 with following json data:
{"errorCode":"2","errorMessage":"My custom exception."}
Obviously, you also need to throw the exception from your controller:
throw new TestException();
and include the exception renderer http://book.cakephp.org/2.0/en/development/exceptions.html#using-a-custom-renderer-with-exception-renderer-to-handle-application-exceptions
This may 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
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With