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:
- /**
- * Initializes the error action to deal with AJAX responses
- *
- * @return null
- */
- public function init()
- {
- $this->_helper->ajaxContext->addActionContext(
- )->initContext();
- }
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:
- $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:
- 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:
- // conditionally display exceptions
- if ($this->getInvokeArg('displayExceptions') == true) {
- $this->view->exception = $errors->exception;
- }
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.
- // conditionally display exceptions
- if ($this->getInvokeArg('displayExceptions') == true) {
- if ($this->_request->isXmlHttpRequest()) {
- $this->view->exception = $errors->exception
- ->getMessage();
- $this->view->stackTrace = $errors->exception
- ->getTrace();
- } else {
- $this->view->exception = $errors->exception;
- }
- }
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
- $this->view->request = $errors->request;
to this:
- if ($this->_request->isXmlHttpRequest()) {
- $this->view->request = $this->_request->getParams();
- } else {
- $this->view->request = $errors->request;
- }
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