In the first part of this blog post we’ve talked about the theory behind REST services. In this post we put the theory into practice with the help of some code snippets you can implement in your own application.
So how exactly do we put everything in practice? We go through the code step by step, but we assume you have a sort-of working Zend application and enough knowledge on Zend Framework. Overall it’s not really that hard.
Create a REST controller
All our functionality is hidden behind our own REST controller. This controller is extended from Zend_Controller_Action. This is done because in our project either a complete controller is REST or it isn’t. There is no mixture between REST and non-REST actions. When you need a simple REST-action then this setup might not be the best case for you. For now, we use the “all actions in controller are REST enabled”-rule.
The only thing we need to add is an additional init() method where we initialize the context-switch.
Create the REST contextswitch helper
The REST contextswitch helper is not that much different as the normal context switch helper. In fact: you could use the normal contextswitcher without any problem. If you have worked with the contextswitch helper, you might have noticed some differences.
As stated earlier, we want all our actions in the REST controllers to be context-aware. We could achieve this during initialization of the controller where we add each action to the context-switcher. This is pretty dull and error-prone work. Instead, we override the getActionContexts(), hasActionContext() and addActionContext() methods. The getActionContext() is called by ZF to decide which context it needs to display, so it would need to return the same context for all actions. The hasActionContext() always returns true, since all actions have a context and addActionContext() must be overridden by a method that will trigger an exception. We cannot use this method for adding contexts, since it would mean we add a context per action. Instead, we must use a new method called addGlobalContext(), which adds a global context, and it’s this context that always gets returned by getActionContext().
Still on our todo list is to implement removeGlobalContext() to, well, remove the global context, and removeActionContext(), which also should trigger an exception. Consider this homework for you as a reader to implement. :)
Furthermore: we need to add a small block of code in the initContext() function: When we don’t have a context, we must add the default context. In order to do this we must copy/paste the whole initContext() block. :( It’s exactly what we wanted to avoid when using an OO setup.
Create an accept-handler plugin
Now the controller is completely finished and we can move on to the plugins. We need some functionality that actually decides what format we need to use. This is done with our accept-handler plugin. This controller-plugin simply implements the routeShutdown() method, but there are some small catches though:
First, we don’t always have an HTTP request. This happens when we call the controller through a CLI SAPI for example. In that case we just return.
Next, we need to set the “Vary” flag in the response. See the first blog post about why this is needed (hint: caching).
The plugin self is nothing more than a parsing of the “Accept”-header and setting the ‘format’ parameter. This way we can control the contextswitch either by Zend’s original contextswitch or by our rest-contex switcher. That’s the only job this plugin must do: set the request’s format-parameter based on the accept-header.
The HTTP_HEADER_APPLICATION_TYPE is a constant that points to the vendor-string:
which means our clients can use context switch through the HTTP fields:
Accept: application/vnd.com.enrise.rest+json; version: 1.0; Accept: application/vnd.com.enrise.rest+xml; version: 1.0; etc..
Create a media-format plugin
Another thing we must do: check if we are actually allowed to view the given context. It might be possible that we don’t accept all contexts on all occasions. For instance, we accept JSON and XML output on production but not HTML output. On a development system, we like to have HTML output, because that would make our life easier.
We could build the “check” into the accept-handler, but we’ve decided we wanted a separate place for that. There are some additional reasons we need this separate, but has no influence for the scope of this blog.
But before we can check what formats are allowed, we need to define them. Our application.ini is a good place for that:
We now know our formats so we can create a simple plugin that fetches the formats and compares them with our current “format” parameter from the request. If it doesn’t match, we are requesting a “forbidden” or unknown format. We should respond with a HTTP 415 (Media type not supported) status code.
That’s it.. make sure though you add the plugins to your front-controller inside your application.ini:
resources.frontController.plugins = "Service_Controller_Plugin_AcceptHandler" resources.frontController.plugins = "Service_Controller_Plugin_Mediaformat"
Doing REST and doing REST right are two completely different things. REST isn’t easy, it just looks that way. Versioning, HATEOAS, stateless resources and actions are much more difficult to implement as you might think. However, with the help of the Zend Framework and some custom code you can come a long way..