Use The Accept Header To Set Your Return Data With Zend Framework 2

In this article, I detail the process by which you can set up your controller actions in Zend Framework 2 to return either the default HTML, or JSON data, depending on the "Accept Header" in the request. It incorporates changes related to a security update added since this very helpful article was written, and expands on some of the intricacies of making your web layer objects better "json providers."

Why do I care?

If you are using some of the many javascript mvc frameworks out there to make your front end shine, it is often necessary to scaffold your view, and then populate it with data asynchronously, or change it depending on user interaction. You could do things like this:

To list all your clams (but I think that's kind of messy), and may very well require some code duplication and extra actions, why not leverage the requests Accept header to determine which data to send back? Then you can use the same route to return the correct view template, or a JSON representation of your data.

Setting Up Zend Framework 2

To begin, we need to add a view strategy to our application model that will allow our controllers to return JSON. In an *.ini style configuration, you can add this line to your module config file:

strategies.json = "ViewJsonStrategy"

Or, if you are using the the module.config.php, then ensure your view manager array contains:

'view_manager' => array(
    'strategies' => array(

Obviously, there will likely be more settings in both cases, but they are omitted for simplicity.

Preparing our Controller

As noted in these release notes, we have to explicitly specify which types of data we want to return to the view. I'll list the full code of the controller action, and then we can go over each area:

public function clamsAction()
    $acceptCriteria = array(
    'Zend\View\Model\ViewModel' => array(
    'Zend\View\Model\JsonModel' => array(

    $viewModel = $this->acceptableViewModelSelector($acceptCriteria);

    Json::$useBuiltinEncoderDecoder = true;

    $clamList = $this->getClamList();

    return $viewModel->setVariables(array("clams" => $clamList));
  • On line 4, we define which types of view models should be created depending on the Accept header, so for an Accept header of application/json, we will return a JsonModel, but for a text/html Accept header, then we just return the regular old ViewModel.

  • On line 12, we pass the accept criteria to the controller plugin acceptableViewModelSelector.

  • On line 14, we tell our application to use the Zend Framework JSON encoder, more on this later.

  • Line 16 simply gets our fictitious web layer object, a ClamList.

  • Finally, on line 18, we add our properties to the view model and return it.

That's pretty much it; now when you browse to the /clams route, you would see whatever view you have defined. Let's assume it sets up some basic layouts and adds some javascript properties to view elements. When the javascript executes and accesses the same route, as long as it passes the correct Accept header, it will return JSON data!

If you wanted to test this example (and you went to the trouble of mocking up this sample application) you could simply run a curl call:

curl -H "Accept: application/json"

Writing Serializable Web Layer Objects

You will notice that I simply threw the $clamList object right into the view model variables, and was done with it. Here, is where I'll explain why we decided to use the Zend Framework JSON encoder instead of the the default PHP one. The json_encode function will encode PHP objects to JSON, but only if their properties are public. In general, it is not advisable to make an object's properties public, but rather provide accessors to the properties. Doing this adds some complication when you want to turn one of your web layer objects into JSON.

However, there is a solution. If the object has a toJson method, the Zend Framework JSON encoder will use that method, expecting a JSON representation to be returned. Now, ` you can control exactly how your web objects are converted to JSON. Maybe you only want to expose a subset of properties, or add additional information that would be helpful for the client developers.

To achieve this in a clean way, we first define a simple interface:

interface JsonProvider
    public function toJson();

And then, have our delicious ClamList object implement it:

class ClamList implements JsonProvider
    private $clams = array();
    private $batchNumber = null;

    public function addClam($clam)
        $this->clams[] = $clam;

    public function toJson()
        $object = new \stdClass();
        $object->batchNumber = $this->batchNumber;
        $object->clams = $this->clams;
        return Json::Encode($object);

The above is a rather simplified example, but the idea is that only inside your toJson method do you assemble an object with public properties, which is ultimately returned as JSON data.

Final Thoughts

I hope you found this discussion helpful to building out a clean web app API. There are likely other ways to achieve this, some things that occurred to me included:

  • Building out a custom view strategy, using reflection to extract the object properties.
  • Creating a controller plugin to transform web objects to JSON.

If you try either of these or something else, please let me know!

If you enjoyed reading this or learned something, please consider sharing via , , or . Thanks!

If you enjoyed this article, you might like others related to the Software interest.