<!--toc--> |
|
## Introduction |
|
[JSON-RPC](http://json-rpc.org/) 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](http://json-rpc.org/wiki/specification) 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" meber |
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 sucesseful 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 |
<!--toc-->
|
|
## Introduction
|
|
[JSON-RPC](http://json-rpc.org/) 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](http://json-rpc.org/wiki/specification) 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" meber
|
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 sucesseful 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` |