RESTful services with Zend Framework (Part 1)
I previously wrote about the opensource project I’ve created: RESTful Zend Framework which is a custom Zend Framework Application built to act as a REST API.
This is the first of couple of follow up articles describing how I built the REST implementation from scratch.
Some Assumptions:
I’m going to skip the initial project creation and assume you already know the basics, if not, spend some time on Google figuring it out first (I don’t recommend the ZF documentation since it’s more confusing than helpful!)
My entire application is meant to be a REST API, I don’t have a mixed mode of web controllers and REST controllers, though it is easily possible to have both, with very little modifications to fit your needs once you read through the steps.
I believe APIs should have maintainable revisions and as such I’ve used Zend Framework Modules to separate my API revisions, again, its easy enough to skip that and suit your own needs.
Configuration:
As always, the first thing you’d wanna do is customize your application.ini file, here are the lines I’ve added:
resources.frontController.defaultModule = "v1" resources.frontController.moduleDirectory = APPLICATION_PATH "/modules" resources.frontController.moduleControllerDirectoryName = "controllers" resources.modules = ""
You should also remove the following line from your application.ini file, otherwise ZF will complain about missing Module Bootstrap calsses.
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
This will enable Modules in your Application and setup the default module name as “v1”. (At this point you would want to re-organize your directory structure to accommodate modules.)
You can skip this step if you don’t want Modules as part of your Application.
Setting up the REST Route:
Now we need to setup routing, ZF comes with its own REST route, so lets make sure all our controllers are setup to go through it by adding the following to our application.ini
resources.router.routes.rest.type = Zend_Rest_Route
The REST route controls the URL scheme for our application, more on that later.
Note: this method will apply the REST route to ALL controllers If you are thinking of having a mix of REST controllers and regular Zend_Action_Controller you need to customize the route rules by initiating it in your Bootstrap.php instead.
At this point, some clean-up of your application directory is probably a good idea …
depending on your set-up of the REST route, you might want to delete the views folder if you apply REST to all your controllers.
I created a new folder named “v1” under application/modulesto match my module setup, and moved the controllers & models folders into it.
Controllers vs. Resources
In a typical ZF Application you would want to have your controllers extend Zend_Controller_Action, but in this case, we want them to extend Zend_Rest_Controller.
class FooController extends Zend_Rest_Controller
Zend_Rest_Controller is just an abstract controller with a list of predefined Actions that we should implement in each of our Controllers:
class FooController extends Zend_Rest_Controller
{
public function indexAction()
{}
public function getAction()
{}
public function postAction()
{}
public function putAction()
{}
public function deleteAction()
{}
}
The first thing you’ll notice is that the Actions represent the most basic HTTP Methods: GET, POST, PUT and DELETE which are the core of our RESTful implementation. at this point you should start thinking of your Controllers as Resources (you might wanna read up on REST to clearify this bit)
Also, we didn’t create any view scripts for this Controller, and as we are not going to need the view renderer anymore, lets disable it:
public function init()
{
$this->_helper->viewRenderer->setNoRender(true);
}
this is a temporary solution and will only disable the view rendered for this controller, we eventually want to disable it for all our REST controllers (covered in later steps).
Now lets add some response output and give it a go!
public function indexAction()
{
$this->getResponse()->setBody('Hello World');
$this->getResponse()->setHttpResponseCode(200);
}
public function getAction()
{
$this->getResponse()->setBody('Foo!');
$this->getResponse()->setHttpResponseCode(200);
}
public function postAction()
{
$this->getResponse()->setBody('resource created');
$this->getResponse()->setHttpResponseCode(200);
}
public function putAction()
{
$this->getResponse()->setBody('resource updated');
$this->getResponse()->setHttpResponseCode(200);
}
public function deleteAction()
{
$this->getResponse()->setBody('resource deleted');
$this->getResponse()->setHttpResponseCode(200);
}
We test this by using CURL in the command line:
curl -v http://localhost/v1/foo curl -v -X GET http://localhost/v1/foo/id curl -v -X POST http://localhost/v1/foo curl -v -X PUT -d '' http://localhost/v1/foo/id curl -v -X DELETE http://localhost/v1/foo/id
you’ll notice that I’ve added /id to GET, PUT, DELETE. This is by design of course, remember the REST route you setup earlier? well this is where it comes to play! now that you are creating Resources not controllers, there are no need for any other actions other than to CREATE, READ, UPDATE & DELETE individual resources, otherwise known as CRUD and naturally: READ, UPDATE & DELETE require a resource identifier so Zend_Rest_Route takes care of mapping the URLs to match. here’s the breakdown of how the HTTP methods map to CRUD functions:
- POST = CREATE
- GET = READ
- PUT = UPDATE
- DELETE = that’s right, you guessed it! DELETE!
That leaves one: The index action handles index/list requests; it should respond with a list of the requested resources.
Now lets modify our logic to give a proper REST response:
public function indexAction()
{
$this->getResponse()->setBody('List of Resources');
$this->getResponse()->setHttpResponseCode(200);
}
public function getAction()
{
$this->getResponse()->setBody(sprintf('Resource #%s', $this->_getParam('id')));
$this->getResponse()->setHttpResponseCode(200);
}
public function postAction()
{
$this->getResponse()->setBody('Resource Created');
$this->getResponse()->setHttpResponseCode(201);
}
public function putAction()
{
$this->getResponse()->setBody(sprintf('Resource #%s Updated', $this->_getParam('id')));
$this->getResponse()->setHttpResponseCode(201);
}
public function deleteAction()
{
$this->getResponse()->setBody(sprintf('Resource #%s Deleted', $this->_getParam('id')));
$this->getResponse()->setHttpResponseCode(200);
}
Now lets test again:
curl -v http://localhost/v1/foo curl -v -X GET http://localhost/v1/foo/1 curl -v -X POST http://localhost/v1/foo curl -v -X PUT -d '' http://localhost/v1/foo/1 curl -v -X DELETE http://localhost/v1/foo/1
There, nice and clean RESTful response!
Now lets add some more HTTP Mehods beyond the basic GET, POST, PUT, DELETE.
Two very popular HTTP methods are: HEAD and OPTIONS.
The OPTIONS method represents a request for information about the communication options available on the request/response chain identified by the Request-URI.
This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval.
The HEAD method is identical to GET except that the server MUST NOT return a message-body in the response. The metainformation contained in the HTTP headers in response to a HEAD request SHOULD be identical to the information sent in response to a GET request. This method can be used for obtaining metainformation about the entity implied by the request without transferring the entity-body itself. This method is often used for testing hypertext links for validity, accessibility, and recent modification (cache).
Zend_Rest_Controller does not force any methods other than ones we’ve already used, and since HEAD and OPTIONS in my example behave the same across all controllers, lets create a new Class REST_Controller and implement the optionsAction and headAction there.
First step is to add the custom REST_ namespace to our application.ini:
autoloaderNamespaces.rest = "REST_"
the REST_Controller class should look something like this:
abstract class REST_Controller extends Zend_Rest_Controller
{
public function headAction()
{
// you should add your own logic here to check for cache headers from the request
$this->getResponse()->setBody(null);
}
public function optionsAction()
{
$this->getResponse()->setBody(null);
$this->getResponse()->setHeader('Allow', 'OPTIONS, HEAD, INDEX, GET, POST, PUT, DELETE');
}
}
now lets make sure our FooController extends the newly created class:
class FooController extends REST_Controller
Now lets test the new actions:
curl -v -X HEAD http://localhost/v1/foo curl -v -X OPTIONS http://localhost/v1/foo/1
Update [Friday, 04 March 2011]: using application.ini to setup the REST route, added OPTIONS, HEAD actions
Update [Sunday, 01 January 2012]: This article is out of date, please refer to the GitHub repo for updated instructions, the same principles still apply, but with some changes and slight modifications.
In the next part I’ll explain how to add context switching and add multi format input/output
Note: all examples used are part of RESTful Zend Framework on GitHub