Create RESTful Applications Using The Zend Framework - Part II : Using HTTP Response Code

In our last example, we used Zend_Rest_Route and Zend_Rest_Controller to demonstrate how to map requests to controller actions. We also used the response object to send text content in the HTTP response. In this article let us send appropriate HTTP response codes using the response object.

RFC 2616 describes HTTP response codes to use in various contexts.

In this example, we will use a few response codes

  • 200 OK - successfully returning the requested articles
  • 201 Created - article has been created
  • 204 No Content - article has been deleted
  • 404 Not Found - no resource found at the requested URI
  • 503 Service Unavailable - the server is experiencing heavy load. Try later.

We will cover other HTTP response codes in upcoming articles on the subject.

Let's start coding. Grab the ArticleContoller.php from the article Create RESTful Applications Using The Zend Framework
.

Demonstrating the usage of HTTP response code 200

Modify the indexAction() of our ArticleController

<?php
public function indexAction()
    {
         
$this->getResponse()
            ->
setHttpResponseCode(200)
            ->
appendBody("all articles content");
    }
?>

The Zend_Controller_Response_Abstract class has the method setResponseCode() using which we can set the HTTP response code. If we weren't using Zend Framework, we would simply use the PHP function header().

In this article, we again use cUrl from the command line to view the response. The -v switch to the curl command prints the output in verbose mode.

curl http://zfrest.example.com/article -v
* About to connect() to zfrest.example.com port 80 (#0)
*   Trying 127.0.0.1... connected
* Connected to zfrest.example.com (127.0.0.1) port 80 (#0)
> GET /article HTTP/1.1
> User-Agent: curl/7.19.7 (i386-redhat-linux-gnu) libcurl/7.19.7 NSS/3.12.4.5 zlib/1.2.3 libidn/1.9 libssh2/1.0
> Host: zfrest.example.com
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Sat, 23 Jan 2010 18:11:08 GMT
< Server: Apache/2.2.13 (Fedora)
< X-Powered-By: PHP/5.2.11
< Content-Length: 20
< Connection: close
< Content-Type: text/html; charset=UTF-8
< 
* Closing connection #0
all articles content

If you observe carefully, the response contains the line

HTTP/1.1 200 OK

Demonstrating the usage of HTTP response code 201

Let's perform HTTP POST action on the article resource. We modify the postAction of ArticleController

<?php
public function postAction()
    {
        
$this->getResponse()
             ->
setHttpResponseCode(201)
            ->
appendBody("created the article\n")
            ->
appendBody("http://zfrest.example.com/article/5");

    }

?>

In the response body we also mention the URI from which the newly created article can be accessed.

The output looks like

curl -d "article=myarticle" http://zfrest.example.com/article/ -v
* About to connect() to zfrest.example.com port 80 (#0)
*   Trying 127.0.0.1... connected
* Connected to zfrest.example.com (127.0.0.1) port 80 (#0)
> POST /article/ HTTP/1.1
> User-Agent: curl/7.19.7 (i386-redhat-linux-gnu) libcurl/7.19.7 NSS/3.12.4.5 zlib/1.2.3 libidn/1.9 libssh2/1.0
> Host: zfrest.example.com
> Accept: */*
> Content-Length: 17
> Content-Type: application/x-www-form-urlencoded
> 
< HTTP/1.1 201 Created
< Date: Sat, 23 Jan 2010 17:52:51 GMT
< Server: Apache/2.2.13 (Fedora)
< X-Powered-By: PHP/5.2.11
< Content-Length: 55
< Connection: close
< Content-Type: text/html; charset=UTF-8
< 
created the article
* Closing connection #0
http://zfrest.example.com/article/5

Demonstrating the usage of HTTP response code 204

Let's delete an article. The user agent does not require any content in the body when the article is deleted.

We modify the deleteAction() of ArticleController

<?php
 
public function deleteAction()
    {
        
$this->getResponse()
            ->
setHttpResponseCode(204);

    }

?>

The output on the command line looks like:

curl -X DELETE http://zfrest.example.com/article/12 -v
* About to connect() to zfrest.example.com port 80 (#0)
*   Trying 127.0.0.1... connected
* Connected to zfrest.example.com (127.0.0.1) port 80 (#0)
> DELETE /article/12 HTTP/1.1
> User-Agent: curl/7.19.7 (i386-redhat-linux-gnu) libcurl/7.19.7 NSS/3.12.4.5 zlib/1.2.3 libidn/1.9 libssh2/1.0
> Host: zfrest.example.com
> Accept: */*
> 
< HTTP/1.1 204 No Content
< Date: Sat, 23 Jan 2010 17:58:14 GMT
< Server: Apache/2.2.13 (Fedora)
< X-Powered-By: PHP/5.2.11
< Content-Length: 0
< Connection: close
< Content-Type: text/html; charset=UTF-8
< 
* Closing connection #0

Demonstrating the usage of HTTP response code 404

Let's try to access article 20 which does not exist.

Modify the getAction() of ArticleController. For the sake of example, let's pretend the getAction() is prepared to respond to only non-existent resources.

<?php 
public function getAction()
    {
        
$this->getResponse()
            ->
setHttpResponseCode(404)
            ->
appendBody("requested article 20 not found");

    }

?>

The output looks like:

curl http://zfrest.example.com/article/20 -v
* About to connect() to zfrest.example.com port 80 (#0)
*   Trying 127.0.0.1... connected
* Connected to zfrest.example.com (127.0.0.1) port 80 (#0)
> GET /article/20 HTTP/1.1
> User-Agent: curl/7.19.7 (i386-redhat-linux-gnu) libcurl/7.19.7 NSS/3.12.4.5 zlib/1.2.3 libidn/1.9 libssh2/1.0
> Host: zfrest.example.com
> Accept: */*
> 
< HTTP/1.1 404 Not Found
< Date: Sat, 23 Jan 2010 18:17:56 GMT
< Server: Apache/2.2.13 (Fedora)
< X-Powered-By: PHP/5.2.11
< Content-Length: 30
< Connection: close
< Content-Type: text/html; charset=UTF-8
< 
* Closing connection #0
requested article 20 not found

Demonstrating the usage of HTTP response code 503

Lastly, let's respond with the HTTP code 503.

Modify the putAction of ArticleController

 public function putAction()
    {
        $this->getResponse()
            ->setHttpResponseCode(503)
            ->appendBody("unable to process put requests. Please try later");
 
    }

The output looks like:

 curl -d "article=updatedarticle" -X PUT http://zfrest.example.com/article/1 -v
* About to connect() to zfrest.example.com port 80 (#0)
*   Trying 127.0.0.1... connected
* Connected to zfrest.example.com (127.0.0.1) port 80 (#0)
> PUT /article/1 HTTP/1.1
> User-Agent: curl/7.19.7 (i386-redhat-linux-gnu) libcurl/7.19.7 NSS/3.12.4.5 zlib/1.2.3 libidn/1.9 libssh2/1.0
> Host: zfrest.example.com
> Accept: */*
> Content-Length: 22
> Content-Type: application/x-www-form-urlencoded
> 
< HTTP/1.1 503 Service Temporarily Unavailable
< Date: Sat, 23 Jan 2010 18:21:48 GMT
< Server: Apache/2.2.13 (Fedora)
< X-Powered-By: PHP/5.2.11
< Content-Length: 48
< Connection: close
< Content-Type: text/html; charset=UTF-8
< 
* Closing connection #0
unable to process put requests. Please try later

When implementing a REST client you could simply examine the HTTP response code to check whether the request was successful. One common area where you can see response codes being examined is AJAX applications.

For your convenience, I am posting the entire code of the ArticleController.php below.


<?php

class ArticleController extends Zend_Rest_Controller
{

    public function 

init()
    {
        
$this->_helper->viewRenderer->setNoRender(true);
    }

    public function 

indexAction()
    {
         
$this->getResponse()
            ->
setHttpResponseCode(200)
            ->
appendBody("all articles content");
    }

    public function 

getAction()
    {
        
$this->getResponse()
            ->
setHttpResponseCode(404)
            ->
appendBody("requested article 20 not found");

    }

    public function 

postAction()
    {
        
$this->getResponse()
             ->
setHttpResponseCode(201)
            ->
appendBody("created the article\n")
            ->
appendBody("http://zfrest.example.com/article/5");

    }

    public function 

putAction()
    {
        
$this->getResponse()
            ->
setHttpResponseCode(503)
            ->
appendBody("unable to process put requests. Please try later");

    }

    public function 

deleteAction()
    {
        
$this->getResponse()
            ->
setHttpResponseCode(204);

    }

}

?>

Summary

  • We learned about some HTTP response codes
  • We used the setResponseCode() method the Zend_Controller_Request_Http object to send HTTP response codes
  • We used cUrl to test the response of our REST enabled ArticleController
Content Type: 
FOSS Project: 

Comments

Hi,
Thanks for the demo. It was perfect.
Can you give a simple tutorial as how we can establish a communication between client-server using API KEYS?

looking forward for Handling of REST API KEYS using Zend Framework

The next article in this series will cover API key handling.

Per the HTTP standard, when sending back a 201 response you also need to use the Location header to specify the new resource created. So rather than appending the new url to the body, you should be adding a new header.

public function postAction()
{
$this->getResponse()
->setHttpResponseCode(201)
->setHeader("Location", "http://zfrest.example.com/article/5")
->appendBody("created the article\n")
}

Thanks for the comment.

You're right.

Actually, more information goes in the response.

Citing the RFC 2616

The response SHOULD include an entity containing a list of resource characteristics and location(s) from which the user or user agent can choose the one most appropriate. The entity format is specified by the media type given in the Content-Type header field.

Just wondering. Because REST services normaly returns just XML, why don't you use some views to output the result?

I think if you want to output JSON this way is great, just say json_encode()

w

The slide 76 + 77 tells me to use index.xml.phtml files and even set the responseheader in the view. I think this is more MVC, than this tutorial. Thanks for your reply.

w

Thanks for the comment.

To keep the focus of the article on HTTP response code, I have disabled view and layout in this example.

You could sure use views.

If you use JSON, you could take advantage of the Json view helper or the Json action helper. The Json action helper disables the view and layout, sets the Content-Type header value to application/json and sends the response.

// Bootstrap.php
<?php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initRestRoute()
{
$this->bootstrap('frontController');
$frontController = Zend_Controller_Front::getInstance();
$restRoute = new Zend_Rest_Route($frontController);
$frontController->getRouter()->addRoute('default', $restRoute);

}
}

// application/controllers/IndexController.php
class IndexController extends Zend_Rest_Controller
{
public function init(){
// nothing rest
$this->_helper->viewRenderer->setNoRender(true);
}

public function indexAction(){
// nothing rest but way of rest
//$this->getResponse()
//->appendBody("From indexAction() returning all articles");
$this->getResponse()
->setHttpResponseCode(201)
->appendBody("created the article\n")
->appendBody("http://zfrest.example.com/article/5");
}
}

Result:
======
[root@www ~]# curl --version
curl 7.15.5 (i686-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5
Protocols: tftp ftp telnet dict ldap http file https ftps
Features: GSS-Negotiate IDN IPv6 Largefile NTLM SSL libz
[root@www ~]# curl -v http://localhost/zf/public/index
* About to connect() to localhost port 80
* Trying 127.0.0.1... connected
* Connected to localhost (127.0.0.1) port 80
> GET /zf/public/index HTTP/1.1
> User-Agent: curl/7.15.5 (i686-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5
> Host: localhost:80
> Accept: */*
>
< HTTP/1.0 500 Internal Server Error
< Date: Thu, 08 Apr 2010 16:26:24 GMT
< Server: Apache/2.2.3 (CentOS)
< X-Powered-By: PHP/5.2.12
< Content-Length: 0
< Connection: close
< Content-Type: text/html; charset=UTF-8
* Closing connection #0
[root@www ~]#

Internal server error indicates that there's a problem with web server setup. Please check your web server error log. It might help you to fix the issue.

I'd recommend you to follow the quick start guide and make sure your application works.

make sure that you implement all the abstract methods in the Zend_Rest_Controller whenever you extend it in your Controllers

public function getAction ()
{
// action content
}
public function postAction ()
{
// action content
}
public function putAction ()
{
// action content
}
public function deleteAction ()
{
// action content
}

How can I get a model class from the controller?
for example, from the index indexAction()....
this doesn't works:
$test = new Application_Model_Test();
it returns an error "Class 'Application_Model_Test' not found"...
I put it in application>modules>api>models.

thank you for this!

How can I get a model class from the controller?
for example, from the index indexAction()....
this doesn't works:
$test = new Application_Model_Test();
it returns an error "Class 'Application_Model_Test' not found"...
I put it in application>modules>api>models.

thank you for this!

Add new comment