This section will guide you through all the code you have to write to create an interactive web application using libzeep. The way this works in libzeep looks a lot like popular frameworks found for Java. If you're familiar with JSP and e.g. Struts, you'll notice the similarities.
It is very inconvenient to write HTML code in C++ directly using string concatenations
and streams. Therefore, a separation has been made. All HTML is put into
XHTML template files. These template files can use special tags to generate
HTML tags based on data provided by the server application. A script language
that looks a lot like JSP 'Expression Language' (or el
in short) is used to program conditional constructs. Communication between
this el
script and the server application is done via
el::object
data objects.
Let's start with a simple hello world example. We first create a template
file, save this file as hello.xhtml
.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE xhtml PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Hello</title> </head> <body> <p>Hello, world!</p> </body> </html>
This is a very simple, strict XHTML 1.1 file. We will serve it with our server:
#include <zeep/http/webapp.hpp> #include <boost/bind.hpp> using namespace std; class MyWebApp : publiczeep::http::webapp
{ public: MyWebApp(); void handle_welcome(constzeep::http::request
& request, constzeep::http::el::scope
& scope,zeep::http::reply
& reply); }; MyWebApp::MyWebApp() { mount("", boost::bind(&MyWebApp::handle_welcome, this, _1, _2, _3)); } void MyWebApp::handle_welcome(constzeep::http::request
& request, constzeep::http::el::scope
& scope,zeep::http::reply
& reply) { create_reply_from_template("hello.xhtml", scope, reply); } int main() { MyWebApp server; server.bind("0.0.0.0", 80); server.run(1); }
By calling mount
with the
empty string, we tell libzeep to redirect all access to the base URL to handle_welcome.
This means that visiting the URL http://localhost/
should now return a page containing the string 'Hello, world!'.
Now lets create a form to pass some data from the browser to the server and
back. Save the following file as form.xhtml
.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE xhtml PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:zeep="http://www.cmbi.ru.nl/libzeep/ml"> <head> <title>Form example</title> </head> <body> <zeep:if test="${name}"> <p>Hello ${name}!</p> </zeep:if> <p>Please enter your name and press the Submit button</p> <form id="myForm" action="salute" method="get"> <label>Name: <input id="name" type="text" name="name" value="${name}"/></label> <input type="submit" value="Submit" /> </form> </body> </html>
We add the zeep prefix to our html tag, it has the value "http://www.cmbi.ru.nl/libzeep/ml"
which is the same as the default value for the ns
parameter
in the zeep::http::webapp
constructor. Note that the input tag has an attribute with value '${data}'.
This is a piece of expression language script. This will be explained below:
#include <zeep/http/webapp.hpp> #include <zeep/http/webapp/el.hpp> #include <boost/bind.hpp> using namespace std; class MyWebApp : publiczeep::http::webapp
{ public: MyWebApp(); void handle_welcome(constzeep::http::request
& request, constzeep::http::el::scope
& scope,zeep::http::reply
& reply); void handle_salute(constzeep::http::request
& request, constzeep::http::el::scope
& scope,zeep::http::reply
& reply); }; MyWebApp::MyWebApp() { mount("", boost::bind(&MyWebApp::handle_welcome, this, _1, _2, _3)); mount("salute", boost::bind(&MyWebApp::handle_salute, this, _1, _2, _3)); } void MyWebApp::handle_welcome(constzeep::http::request
& request, constzeep::http::el::scope
& scope,zeep::http::reply
& reply) { create_reply_from_template("form.xhtml", scope, reply); } void MyWebApp::handle_salute(constzeep::http::request
& request, constzeep::http::el::scope
& scope,zeep::http::reply
& reply) { zeep::http::parameter_map params; get_parameters(scope, params); string name = params.get("name", "").as<string>();zeep::http::el::scope
sub(scope); sub.put("name", name); create_reply_from_template("form.xhtml", sub, reply); } int main() { MyWebApp server; server.bind("0.0.0.0", 80); server.run(1); }
This time, we add a new handler for the 'salute' page. The form has an action
that points to this salute page. As can be seen in the handle_salute
method, we first collect the parameters passed in. Parameters are accessed
by name. We then create a sub scope of the scope passed in. In this sub scope
we put the value of the parameter so that the XHTML processor can access
it. And then we return a reply based on the contents of the form.xhtml
template and the contents of the sub scope we created.
el
means Expression
Language. It is a script language that tries to be like http://en.wikipedia.org/wiki/Unified_Expression_Language.
The objects can be created in the C++ server code using the zeep::http::el::object
class. Object created this way are then stored in an zeep::http::el::scope
object and passed along to the XHTML processing code.
zeep::http::el::object
objects can contain
simple data and arrays. E.g., to create an array you could write:
using namespace zeep::http; vector<el::object> ints; for (int i = 0; i < 10; ++i) { el::object int_object; int_object["value"] = i; ints.push_back(int_object); } scope.put("ints", el::object(ints));
And then you can access this in the XHTML:
1: ${ints[1].value}, 2: ${ints[2].value}
Which should output "1: 1, 2: 2"
The method create_reply_from_template
takes the name of a template file and a scope to generate the output XHTML.
We've already seen that el
scripts are processed by this method. But there is more, several special
tags in the template are processed in a special way. These tags are in a
separate XML namespace. You can change this name space using the ns
parameter in the zeep::http::webapp
constructor, the default
is http://www.cmbi.ru.nl/libzeep/ml
.
In the template for the form example above you might have noticed the <zeep:if>
tag. This tag takes one attribute called test
and the value of this tag is interpreted as a el
script. If the script evaluates to something other than empty, zero or false,
the content of the <zeep:if>
tag is included, otherwise
it is discarded in the output.
There are several predefined processing tags which are summarized below.
You can also add your own processing tags using the zeep::http::webapp::add_processor
method. This
method takes a std::string
parameter for the name of the tag
and a processor_type
parameter
which is a call back function.
Table 1. List of predefined processing tags
tag name (without prefix) |
Description |
Example |
---|---|---|
include |
Takes one parameter named |
|
if |
Takes one parameter named |
<zeep:if test="${not empty name}"> Hello ${name} </zeep:if>
|
iterate |
Takes two parameters, |
<ul><zeep:iterate collection="${names}" var="name"> <li>${name}</li> </zeep:iterate></ul>
|
for |
Takes three parameters. The parameters |
<zeep:for begin="1" end="3" var="i"> ${i}, </zeep:for>
|
number |
Format the number in the |
<zeep:number n="1024" f="0.00#B"/> Will output 1K
|
options |
This tag will insert multiple |
<zeep:options collection="${names}" value="id" label="fullName" selected="1" />
|
option |
Generate a single |
<zeep:option value="1" selected="${user}"> John Doe </zeep:option>
|
checkbox |
Create an |
<zeep:checkbox name='cb1' checked='${true}'> Check me </zeep:checkbox>
|
url |
The url processing tag creates a new variable in the current scope
with the name as specified in the |
<zeep:url var="next"> <zeep:param name='page' value='${page + 1}'/> <zeep:url> <a href="${next}">Next page</a>
|
param |
see |
|
embed |
This tag takes the content of the |
<zeep:embed var="<em>hello, world!</em>"/>
|