PrevUpHomeNext

Creating a Web Application

Introduction
The building blocks
Hello world!
Using forms
Using el script
Processing Tags

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 : public zeep::http::webapp
{
  public:
    MyWebApp();

    void handle_welcome(const zeep::http::request& request,
           const zeep::http::el::scope& scope, zeep::http::reply& reply);
};

MyWebApp::MyWebApp()
{
  mount("",      boost::bind(&MyWebApp::handle_welcome, this, _1, _2, _3));
}

void MyWebApp::handle_welcome(const zeep::http::request& request,
           const zeep::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 : public zeep::http::webapp
{
  public:
    MyWebApp();

    void handle_welcome(const zeep::http::request& request,
           const zeep::http::el::scope& scope, zeep::http::reply& reply);
    void handle_salute(const zeep::http::request& request,
           const zeep::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(const zeep::http::request& request,
           const zeep::http::el::scope& scope, zeep::http::reply& reply)
{
  create_reply_from_template("form.xhtml", scope, reply);
}

void MyWebApp::handle_salute(const zeep::http::request& request,
           const zeep::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 file and replaces the tag with the processed content of this file

<zeep:include file="menu.xhtml"/>

if

Takes one parameter named test containing an el script. This script is evaluated and if the result is not empty, zero or false, the content of the if tags is inserted in the output. Otherwise, the content is discarded.

<zeep:if test="${not empty name}">
  Hello ${name}
</zeep:if>

iterate

Takes two parameters, collection which contains an el script that evaluates to an array el::object and a name in var. The content of the iterate tag is included for each value of collection and var will contain the current value.

<ul><zeep:iterate collection="${names}" var="name">
  <li>${name}</li>
</zeep:iterate></ul>

for

Takes three parameters. The parameters begin and end should evaluate to a number. The parameter var contains a name that will be used to hold the current value when inserting the content of the for tag in each iteration of the for loop between begin and end.

<zeep:for begin="1" end="3" var="i">
  ${i},
</zeep:for>

number

Format the number in the n parameter using the f format. This is limited to the formats '#.##0' and '#.##0B' for now. The first formats an integer value using thousand separators, the second tries to format the integer value in a power of two multiplier (kibi, mebi, etc.) with a suffix of B, M, G, etc.

<zeep:number n="1024" f="0.00#B"/>
Will output 1K

options

This tag will insert multiple <option> tags for each element in the collection parameter. This collection paramter can contain an array of strings or it can contain an array of el::object. In the latter case, you can specify a value and label parameter to name the value and label fields of these objects. A selected parameter can be used to select the current value of the options.

<zeep:options collection="${names}"
  value="id" label="fullName" selected="1" />

option

Generate a single <option> tag with a value as specified in the value parameter. If selected evaluates to the same value as value, the option is selected. The content of the <option> tag is inserted in the final tag.

<zeep:option value="1" selected="${user}">
  John Doe
</zeep:option>

checkbox

Create an <input> tag with type checkbox. The parameter name is used as name attribute and the parameter checked is evaluated to see if the checkbox should be in checked mode.

<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 var parameter. It then creates a list of all original HTTP parameters for the current page. You can override these parameter, and add new ones, by adding <param> tags in the <url> tag.

<zeep:url var="next">
  <zeep:param name='page' value='${page + 1}'/>
<zeep:url>
<a href="${next}">Next page</a>

param

see url above.

 

embed

This tag takes the content of the var parameter which should contain valid XML and puts the processed value in the document.

<zeep:embed var="&lt;em&gt;hello, world!&lt;/em&gt;"/>



PrevUpHomeNext