Home > Tutorials > Changing ErrorController to work with AJAX

Changing ErrorController to work with AJAX

Posted by dstockto on August 10, 2011

If you've ever built a Zend Framework MVC app which makes AJAX calls, you may have noticed that if an error occurs, you'll get a chunk of JSON followed by the HTML for the error page. If you've built a layout, you'll get all of that back too. This is fine if your users hit the page in the browser but it can cause problems with your JavaScript being able to correctly decode your JSON.

Fortunately, changing this so that you only get JSON if it's an AJAX call is pretty simple.
Open your /application/controllers/ErrorController.php file in your favorite editor.
We want to register the errorAction in an AJAX context. This will tell Zend Framework that if it detects an XML HTTP Request and there's a parameter called "format" with a value of "json" that it should automatically turn off the view and the layout. This will leave us with just the JSON part of the response.

If you don't have an init method in your ErrorController class, create one like this:

  1. /**
  2.  * Initializes the error action to deal with AJAX responses
  3.  *
  4.  * @return null
  5.  */
  6. public function init()
  7. {
  8. $this->_helper->ajaxContext->addActionContext(
  9. 'error', array('json')
  10. )->initContext();
  11. }

This bit of code does most of the work, but chances are you may want to add a few more useful pieces of information to your JSON response. Typically in any AJAX calls I'll have a success variable that indicates whether the call I made worked or not. In this case since everything going the the ErrorController probably had an error, it's safe to assume success should be false.

Add this line to the top of the errorAction method:

  1. $this->view->success = false;

To test this out, you can have your action your AJAX call hits throw an exception that you don't catch. This is as simple as:

  1. throw new Exception('Foo');

If you do this, and view the JSON in chrome or Firebug, you'll see a success = false, an empty exception object, an empty request object and a very helpful message with a value of "Application error". 

The empty request and exception object are because those are PHP objects that are getting set into the view within the ErrorController. In a JSON response, it may be helpful to add some more information from within those objects.

Near the end of the errorAction method, you should see some code like:

  1. // conditionally display exceptions
  2. if ($this->getInvokeArg('displayExceptions') == true) {
  3. $this->view->exception = $errors->exception;
  4. }

If you want this additional information to only be sent to the view if displayExceptions is turned on and if it's an AJAX call, you can make the following modifications.

  1. // conditionally display exceptions
  2. if ($this->getInvokeArg('displayExceptions') == true) {
  3. if ($this->_request->isXmlHttpRequest()) {
  4. $this->view->exception = $errors->exception
  5. ->getMessage();
  6. $this->view->stackTrace = $errors->exception
  7. ->getTrace();
  8. } else {
  9. $this->view->exception = $errors->exception;
  10. }
  11. }

This code will replace that empty exception object with the message from the exception. It will also create a new stackTrace object in the JSON which is an array of objects containing information about the stack trace.  It's completely up to you if you want to include that information or not.

The other empty object showing up in the JSON is the empty request. If knowing more about the request would be useful, change this

  1. $this->view->request = $errors->request;

to this:

  1. if ($this->_request->isXmlHttpRequest()) {
  2. $this->view->request = $this->_request->getParams();
  3. } else {
  4. $this->view->request = $errors->request;
  5. }

This chunk of code will replace that empty request object with an array of key: value pairs for all of the parameters in the request.

That's all there is to it. Your AJAX calls will now get a JSON response even if an error happens in one of your controllers.

I hope this helps and I look forward to hearing from you.

Thanks,
David

Comments:

Posted by Nate on
Does this assume that you are properly using context switching in all of your controllers and that you are using one common variable in the url to indicate the context?
Posted by Rob on
Nate, yes... it appears that way. If you're not using ContextSwitch/AjaxSwitch, then you can just check the request for an XHR.
Posted by Wil Moore III on
@Nate:

I don't believe this depends on you using ContextSwitch/AjaxSwitch in all controllers since this gets picked-up by the ErrorController. Of course, if you aren't using an ErrorController, you are out of luck but you should be good if you are.
Posted by dstockto on
This should work as usual if it's a non-ajax call. Since it's using the AjaxContext in the error controller, it would require the incoming request to have a parameter of {format: 'json'}.

If you're making AJAX calls into a Zend Framework application an not using the AjaxContext, the ErrorController could be modified to detect the Ajax calls in whatever way you're making them.

Personally I find the AjaxContext to be a pretty convenient way to keep the logic the same and just vary the view.
Posted by sboucher on
Nice post!

This should be in ZF by default.
Posted by dstockto on
@sboucher: Thank you very much. You are too kind.
Posted by Nate on
Where's the code?
Posted by dstockto on
The link to the code is on the right sidebar. It's in bitbucket.
Leave a Reply



(Your email will not be publicly displayed.)

Please type the letters and numbers shown in the image.Captcha CodeClick the image to see another captcha.