Pretty print JSON in your REST API

Using the Zend Framework 2's AstractRestfulController allows you to quickly set up a RESTful interface. However, in my development environment I was looking for an option to quickly enable pretty print in the JSON output. Zend Framework does not support this by default, but it is easy to implement this with events.

The JSON is rendered by a so-called strategy. This strategy invokes the renderer which creates the JSON for you. In this process there isn't much you can do with options or configuration, but it is extremely easy to reformat the rendered JSON.

Zend\Json has a method to reformat a given JSON string with pretty print. I'll use this to reformat the rendered JSON and make it more readable. To be sure the reformatting is requested by the client, check for the following conditions:

  1. The response should send the response as JSON already
  2. The request should contain a custom header
It is important to check both, as the response could contain html or something else which is not JSON at all. Besides, you only want to pretty print when the user request this. The easiest way is to listen for EVENT_FINISH in your Module class and reformat the response in case both checks are passed.

In code, this looks like this method in your Module.php module class:

use Zend\Mvc\MvcEvent;
use Zend\Json\Json;

public function onBootstrap(MvcEvent $e)
{
    $app = $e->getApplication();
    $em  = $app->getEventManager();

    $em->attach(MvcEvent::EVENT_FINISH, function ($e) {
        $response = $e->getResponse();
        $headers  = $response->getHeaders();
        if (!$headers->has('Content-Type')) {
            return;
        }

        $contentType = $headers->get('Content-Type');
        $value       = $contentType->getFieldValue();
        if (false !== strpos('application/json', $value)) {
            return;
        }

        $request = $e->getRequest();
        $headers = $request->getHeaders();
        if (!$headers->has('X-Pretty-Json')) {
            return;
        }

        $body = $response->getContent();
        $body = Json::prettyPrint($body, array(
            'indent' => '  '
        ));
        $body = $body . "\n";
        $response->setContent($body);
    });
}

Step-by-step

What happens here, are the following steps:

First, you wait for the "finish" event. This means: the routing, dispatching and rendering has been done. Now it's only to complete the request and return the response. We catch the response during "finish" and modify it:

public function onBootstrap(MvcEvent $e) {
    $app = $e->getApplication();
    $em  = $app->getEventManager();

    $em->attach(MvcEvent::EVENT_FINISH, function ($e) {
        // ...
    });
}

The response is taken from the event and a check takes place whether the Content-Type header actually exists (this is required for the next step):

$response = $e->getResponse();
$headers  = $response->getHeaders();

if (!$headers->has('Content-Type')) {
    return;
}

A check takes place to be sure the response is formatted with the content type application/json. If you mix & match html and json, this will prevent the html from being touched:

$contentType = $headers->get('Content-Type');
$value       = $contentType->getFieldValue();

if (false !== strpos('application/json', $value)) {
    return;
}

Then the request header is checked. The value does not matter here, it only is important the header is send with the request:

$request = $e->getRequest();
$headers = $request->getHeaders();

if (!$headers->has('X-Pretty-Json')) {
    return;
}

Finally the contents of the response is taken, reformatted by Zend\Json\Json and then inserted again in the response:

$body = $response->getContent();
$body = Json::prettyPrint($body, array(
    'indent' => '  '
));
$body = $body . "\n";
$response->setContent($body);

Two things to notice here: first, indentation happens with two spaces. The default is \t (tab character), but that makes the output too wide on my console.

Secondly, I append a new line at the end of the response. Normally, the CLI prompt is at the same line as the last line of the JSON string, so it does not start at the same place for every call. Appending \n helps to reset the prompt at a new line.

I hope it makes clear how to enable pretty printing in JSON. This makes it easier to debug problems and read the response directly from the output.