Connecting a JavaScript Web Application to a C++ Backend With Remoting NG and JSON-RPC
Applied Informatics Remoting NG 2013.2 contains a new JSON-RPC transport that implements the JSON-RPC 2.0 protocol. JSON-RPC is a stateless, light-weight remote procedure call (RPC) protocol using JSON as serialization format and HTTP or WebSockets as transport. This can be used to make a JavaScript web application talk to a C++ backend, RPC-style, by simply invoking the server's C++ methods. The JSON-RPC transport can be used like any other Remoting NG transport, either as the only transport in an application, or together with other transports. In order to show how to use the JSON-RPC transport we're going to extend the Pizzeria sample that ships with Remoting NG and normally uses SOAP, to also provide its services via JSON-RPC. To do so, we first need to extend the server so that it creates and registers a JSON-RPC listener in addition to the existing SOAP transport. We could also remove the SOAP transport and only use the JSON-RPC transport as well. Creating and registering the JSON-RPC transport is done with the following code fragment:
Poco::RemotingNG::JSONRPC::Listener::Ptr pJSONRPCListener =
new Poco::RemotingNG::JSONRPC::Listener("0.0.0.0:8081");
pJSONRPCListener->enableCompression(true);
pJSONRPCListener->enableCORS(true);
std::string jsonrpcListener =
Poco::RemotingNG::ORB::instance().registerListener(pJSONRPCListener);
Note that when configuring the JSON-RPC Listener, we're enabling CORS (Cross Origin Resource Sharing) support in the Listener. This will allow a web page hosted on another server (or opened in the browser from a local file) to invoke the JSON-RPC service, provided that the web browser supports CORS (most modern browsers do). We also enable compression support, which will allow the server to send responses back using gzip Content-Encoding if the browser indicates support for it.
After registering the Listener, the service objects must be registered with the Listener as well, to make them available using the JSON-RPC 2.0 protocol:
std::string uri3 = Pizzeria::PizzaDeliveryServiceServerHelper::registerObject(
new Pizzeria::PizzaDeliveryService, "ThePizzeria", jsonrpcListener
);
std::string uri4 = Pizzeria::PizzaPickupServiceServerHelper::registerObject(
new Pizzeria::PizzaPickupService, "ThePizzeria", jsonrpcListener
);
The complete modified source code for PizzeriaServer.cpp can be downloaded at the end of this post.
Now that the server part is done, we can concentrate on the client implementation. There are various JSON-RPC 2.0 implementations available for JavaScript, so we don't have to write our own. In the example we're going to use the JsonRpcClient plugin for JQuery.
First we have to create a JsonRpcClient object, passing the URI of our server object as argument:
var jsonrpc = new $.JsonRpcClient(
{
ajaxUrl: 'http://localhost:8081/jsonrpc/PizzaDeliveryService/ThePizzeria'
}
);
We can then use the call method of JsonRpcClient to send the actual request. As parameters we have to provide the name of the method on the server, and the method's arguments. There are actually two ways to pass arguments in JSON-RPC 2.0. The first one is by-position, using an Array. In this case, the order of the parameters in the array must exactly match the order expected by the server.
var tunaPizza = {
basePrice: 700,
details: {
calories: 390,
carboHydrates: 15.3,
percentFat: 38.2
},
name: 'Tuna',
toppings: [
{
name: 'Tuna',
price: 70,
addInfo: ''
},
{
name: 'Cheese',
price: 50,
addInfo: ''
}
]
};
var address = {
personName: 'Bart Simpson',
streetName: 'Evergreen Terrace',
houseNo: 742,
cityName: 'Springfield',
zip: 12345,
state: 'NT',
phoneNo: '1-636-555-1234',
details: {
milesAway: 10
}
};
jsonrpc.call(
'order', [ address, tunaPizza ],
function(result) { alert('Pizzeria answered: ' + $.toJSON(result)); },
function(error) { console.log('There was an error', $.toJSON(error)); }
);
The second way is by-name. Here we pass an Object as argument, with each member corresponding to a method parameter. The names of the parameters must exactly match the names expected by the server.
jsonrpc.call(
'order', { pizza: tunaPizza, deliverTo: address },
function(result) { alert('Pizzeria answered: ' + $.toJSON(result)); },
function(error) { console.log('There was an error', $.toJSON(error)); }
);
In both cases, the response to a successful call will be a JavaScript object containing the server's return value and output arguments, if any. In our simple example, we simply show the result in an alert box.
Here's the source code for the sample: