Creating a JSON-RPC server with Zend Framework

For my latest project, I was determined to avoid confusing Ajax requests by implementing my own JSON-RPC. Again, Zend Framework has made this incredibly easy.

The documentation recommends not running your Ajax request through the MVC. At first I was a little concerned, but it makes sense as the MVC does add a lot of unnecessary overhead.

I found this blog very helpful in figuring out how to do this.
The first step is to create a new bootstrap file.
I created one in public/api/1.0/jsonrpc.php

Define path to application directory
defined('APPLICATION_PATH')
    || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../../../application'));

// Define application environment
defined('APPLICATION_ENV')
    || define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'production'));

// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
    realpath('../../../library'),
    get_include_path(),
)));

/** Zend_Application */
require_once 'Zend/Application.php';

// Create application, bootstrap, and run
$application = new Zend_Application(
    APPLICATION_ENV,
    APPLICATION_PATH . '/configs/application.ini'
);

$application->getBootstrap()
            ->bootstrap('doctrine')
            ->bootstrap('config');

// Instantiate server, etc.
$server = new Zend_Json_Server();
$server->setClass('App_Model_JsonRpc');

if ('GET' == $_SERVER['REQUEST_METHOD']) {
    // Indicate the URL endpoint, and the JSON-RPC version used:
    $server->setTarget('/api/1.0/jsonrpc.php')
           ->setEnvelope(Zend_Json_Server_Smd::ENV_JSONRPC_2);

    // Grab the SMD
    $smd = $server->getServiceMap();

    // Return the SMD to the client
    header('Content-Type: application/json');
    echo $smd;
    return;
}

$server->handle();

You may want to modify the $application->getBootstrap() line and add/remove bootstrap methods depending on your requirements. I need access to my database within my JSON-RPC server and use Doctrine as my ORM so I bootstrap that.

The most important line is:

$server->setClass('App_Model_JsonRpc');

This tells the server what class should handle all the JSON requests.

I also added a RedirectRule in my .htaccess file to hide the php extension. It’s unnecessary but the .php extension isn’t seen anywhere else on my site so I don’t want it on my JSON RPC server either.
I added this line…

RewriteRule ^api/([0-9].[0-9])/jsonrpc$ /api/$1/jsonrpc.php [NC,L]

…between these two lines:

RewriteRule ^.*$ - [NC,L]
RewriteRule ^api/([0-9].[0-9])/jsonrpc$ /api/$1/jsonrpc.php [NC,L]
RewriteRule ^.*$ index.php [NC,L]

[ad name=”Google Adsense 468×60″]
I then created a class called App_Model_JsonRpc and put it in my models directory.
/application/models/JsonRpc.php

class App_Model_JsonRpc
{

    /**
     * Return sum of two variables
     *
     * @param  int $x
     * @param  int $y
     * @return array
     */
    public function add($x, $y)
    {
        return  $x + $y;
    }
}

The doc blocks are very important as Zend_Json_Server generates the SMD based on the contents of the Doc block.

That’s it for the server. Send it the proper JSON code and it will add 2 numbers together and return the result.

Heres a quick example of how to use your new server.
I recommend installing the ZendJsonRpc jQuery plugin as it makes talking to your server much easier. You can download the plugin here.

<script src="/js/json2.js" type="text/javascript"></script>
<script src="/js/jquery.zend.jsonrpc.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function(){
 myApi = jQuery.Zend.jsonrpc({url: '/api/1.0/jsonrpc'});

 alert('5+5=' + myApi.add(5,5));

 });
</script>

That’s it!
[ad name=”Google Adsense 468×60″]

Share

4 comments

  1. Thanks for sharing.

    I followed your article but I encountering “Internal Server Error” when I try to access /api/1.0/jsonrpc from the browser. Therefore, the Javascript script can’t work either.

    I’m quite sure something is wrong with my .htaccess:
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} -s [OR]
    RewriteCond %{REQUEST_FILENAME} -l [OR]
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^.*$ – [NC,L]
    RewriteRule ^api/([0-9].[0-9])/jsonrpc$ /api/$1/jsonrpc.php [NC,L]
    RewriteRule ^.*$ index.php [NC,L]

    And my bootstrap.php is as follows:

  2. What I meant is my jsonrpc.php is as follows:
    setClass(‘Simple’);

    if (‘GET’ == $_SERVER[‘REQUEST_METHOD’]) {
    // Indicate the URL endpoint, and the JSON-RPC version used:
    $server->setTarget(‘/api/v1/jsonrpc.php’)
    ->setEnvelope(Zend_Json_Server_Smd::ENV_JSONRPC_2);

    // Grab the SMD
    $smd = $server->getServiceMap();

    // Return the SMD to the client
    header(‘Content-Type: application/json’);
    echo $smd;
    return;
    }

    $server->handle();

    (the missing part compared to yours is already taken cared by my index.php)

  3. Thanks for this post.

    I’ve recently done something similar, but I’m trying to figure out how to handle errors for when calls are made with incorrect parameters. Currently my app throws a Zend_Json_Server_Error exception when there are missing parameters but doesn’t return it as a JSON-RPC response. Unfortunately it just spits the exception out as text.

    Any thoughts about how to deal with this?

  4. If you’re having the same problem as Jeremy, try this:

    try {
    // Handle the request:
    $server->handle();
    } catch (Zend_Server_Exception $e) {
    $request = $server->getRequest();
    $response = $server->getResponse();
    $response->setServiceMap($server->getServiceMap());

    if (null !== ($id = $request->getId())) {
    $response->setId($id);
    }
    if (null !== ($version = $request->getVersion())) {
    $response->setVersion($version);
    }
    $server->fault($e->getMessage());

    echo $server->getResponse();
    }

Leave a Reply

Your email address will not be published. Required fields are marked *