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:
- The response should send the response as JSON already
- 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.