JSON RPC
Introduction
JSON-RPC is remote procedure call protocol implemented using JSON data.
It is a lightweight protocol that allows easy Ajax based communication between the browser and the C++ application.
CppCMS implements JSON-RPC 1.0 specifications over HTTP protocol.
JSON-RPC Service
Methods
Let's create a simple RPC service that allows us to add and divide numbers.
JSON-RPC Service should be derived from cppcms::rpc::json_rpc_server
that on the other hand
is derived from cppcms::application
.
class json_service : public cppcms::rpc::json_rpc_server { public:
When we use JSON-RPC service we bind between PRC methods and the application member functions, similarly to what we done for URL dispatching.
In our case:
json_service(cppcms::service &srv) : cppcms::rpc::json_rpc_server(srv) { bind("sum",cppcms::rpc::json_method(&json_service::sum,this),method_role); bind("div",cppcms::rpc::json_method(&json_service::div,this),method_role); }
Similarly to URL mapper we connect the "sum" member function to "sum" JSON-RPC method and specify that this is a method that should return a value (not notification)
Now we implement our service:
void sum(int x,int y) { return_result(x+y); } void div(int x,int y) { if(y==0) return_error("Division by zero"); else return_result(x/y); }
Please note, out functions create two types of the response: normal result response and error response as defined by JSON-RPC specification.
The parameters of the method call are automatically
converted to appropriate C++ object. If the conversion
can't be done, the error would be automatically returned
and the functions would not be called. Similarly integer
result is automatically converted to cppcms::json::value
Similarly to the way we mount cppcms::application
based
classes we create our JSON-RPC service:
cppcms::service srv(argc,argv); srv.applications_pool().mount( cppcms::applications_factory<json_service>()); srv.run();
Client Side Code
Let's create a small form that would handle RPC request for us:
<form onsubmit="return call();"> <p> <input type="text" id="x" /> <input type="submit" value="/" /> <input type="text" id="y" /> = <span id="result"></span> </p> </form>
Now let's implement JSON-RPC call using XmlHTTPRequest - function call:
function call() { ... }
First we create the XMLHttpRequest object and set essential content-type "application/json":
var xhr = new XMLHttpRequest(); xhr.open("post", '/rpc'); // Required by JSON-RPC over HTTP xhr.setRequestHeader("Content-Type","application/json");
Then we configure our JSON-RPC request:
// It is better to use real formatter like JSON.js var x=parseInt(document.getElementById('x').value); var y=parseInt(document.getElementById('y').value); var request = '{"method":"div","params":[' + x + ',' + y +'],"id":1}';
Define onreadystatechange
callback and send the request:
xhr.onreadystatechange = function() { ... } xhr.send(request); return false;
The callback function would look like:
if (xhr.readyState === 4) { var res; if(xhr.status === 200) { // Don't call eval in real code use some parser var result = eval('(' + xhr.responseText + ')'); if(result.error==null) { res = result.result; } else { res = result.error; } } else { res = 'Invalid Status ' + xhr.status; } document.getElementById('result').innerHTML = res; }
Upon successful completion of XHR we parse the response and setup the result value.
Notifications
JSON-RPC specifies two types of remote calls:
- methods - the calls that return certain values
- notification -
void
like calls that do not return result.
The two functions above (div and sum) were methods. If we want to have notifications we can create them as simple as methods:
bind("notify",cppcms::rpc::json_method(&json_service::notify,this),notification_role); bind("both",cppcms::rpc::json_method(&json_service::both,this));
Notification method notify
function should not call return_result
or return_error
, on the other hand both
should return the result if required:
void notify(std::string msg) { std::cout << "We got notification " << msg << std::endl; } void both(std::string msg) { if(notification()) std::cout << "We got notification " << msg << std::endl; else return_result("call:"+msg); }
JSON-PRC Clients
There are many client side toolkits like Dojo that provide sophisticated JSON-RPC clients. Feel free to choose whatever client you like as long as it supports JSON-RPC 1.0.
Note: JSON-RPC 1.0 is the only valid standard, JSON-RPC 2.0 is still draft and relays on JSON-Schema that is draft-standard as well, that is why it is not implemented.
You can also find a simple JSON-RPC client in CppCMS
sources under contrib/client_side/jsonrpc