Applications Hierarchy
General
The basic approach of mapping an URL to a member function of our class works well for small applications, but can become problematic for complex systems.
CppCMS allows you to create a hierarchy of applications that work as a single unit. You connect them by using the url_mapper
and url_dispatcher
classes.
Before you read this tutorial, please read URL Dispatching and Mapping tutorial.
Example
Hierarchy
We will create a very simple example of an "educational" application, that consists of two sub-applications:
- myapp - main application
- numbers - display different numbers:
- all
- odd
- even
- prime
- letters - display different letters:
- all
- capital
- small
- numbers - display different numbers:
We will create 3 classes where myapp
would be the topmost class in the hierarchy, and numbers
and letters
would be sub-applications.
Sub-Applications
Let's define our sub-applications:
class numbers : public cppcms::application { public:
The constructor:
numbers(cppcms::service &srv) : cppcms::application(srv) { dispatcher().assign("",&numbers::all,this); mapper().assign(""); dispatcher().assign("/odd",&numbers::odd,this); mapper().assign("odd","/odd"); dispatcher().assign("/even",&numbers::even,this); mapper().assign("even","/even"); dispatcher().assign("/prime",&numbers::prime,this); mapper().assign("prime","/prime"); }
Create default application URL - all, and connect 4 member functions odd
, even
, prime
and all
to it, such that:
void prime() { response().out() << "2,3,5,7,..."; } void odd() { response().out() << "1,3,5,7,9,..."; } void even() { response().out() << "2,4,6,8,10,..."; }
Now let's create our all
member function that would
include links to different modules:
void all() { response().out() << "<a href='" << url("/") << "'>Top</a><br>" << "<a href='" << url("/letters")<< "'>Letters</a><br>" << "<a href='" << url(".") << "'>All Numbers</a><br>" << "<a href='" << url("odd") << "'>Odd Numbers</a><br>" << "<a href='" << url("even") << "'>Even Numbers</a><br>" << "<a href='" << url("prime") << "'>Prime Numbers</a><br>" << "1,2,3,4,5,6,7,8,9,10,..."; }
Note:
url("/")
- points to the default location of the topmost application. Thenumbers
application does not need to know what that is.url("/letters")
- points to the default location of the sub-application letters starting from the root.url(".")
- points to the default handling for the current application.url("odd")
,url("even")
,url("prime")
- point to current application's different parts.
Now we create a similar class for letters
class letters : public cppcms::application {
public:
Constructor:
letters(cppcms::service &srv) : cppcms::application(srv) { dispatcher().assign("",&letters::all,this); mapper().assign(""); dispatcher().assign("/capital",&letters::capital,this); mapper().assign("capital","/capital"); dispatcher().assign("/small",&letters::small,this); mapper().assign("small","/small"); }
Member functions:
void all() { response().out() << "<a href='" << url("/") << "'>Top</a><br>" << "<a href='" << url("/numbers")<< "'>Numbers</a><br>" << "<a href='" << url(".") << "'>All Letters</a><br>" << "<a href='" << url("capital") << "'>Capital Letters</a><br>" << "<a href='" << url("small") << "'>Small Letters</a><br>" << "Aa, Bb, Cc, Dd,..."; } void capital() { response().out() << "A,B,C,D,..."; } void small() { response().out() << "a,b,c,d,..."; }
Main Application
Now lets create our topmost application:
class myapp: public cppcms::application { public:
Constructor:
myapp(cppcms::service &srv) : cppcms::application(srv) { attach( new numbers(srv), "numbers", "/numbers{1}", // mapping "/numbers(/(.*))?", 1); // dispatching attach( new letters(srv), "letters", "/letters{1}", // mapping "/letters(/(.*))?", 1); // dispatching dispatcher().assign("",&myapp::describe,this); mapper().assign(""); // default URL mapper().root("/myapp"); }
Note: we call attach
member function with following parameters:
new numbers(srv)
New sub-application - we pass the ownership to the parent so we don't need to handle its lifetime on our own.
"numbers", "/numbers{1}"
Mapping - application named "numbers" is mapped according
the the pattern "/numbers{1}" - such that each sub application's URL would replace the {1}
placeholder.
"/numbers(/(.*))?", 1)
The regular expression pattern for matching the sub-application. Note, unlike with member functions, exactly one sub-expression is expected - this captured subexpression is passed to the child's main function for further dispatching.
The same is done for the letters
and myapp
member functions.
Now lets analyze how an HTTP request GET /myapp/numbers/prime
is parsed.
- The web server splits
/myapp/numbers/prime
to/myapp
asSCRIPT_NAME
and/numbers/prime
asPATH_INFO
myapp
matches/numbers/prime
against the/numbers(/(.*))?
expression, and extracts/prime
as the subexpression that is passed tonumbers
's main function./prime
is matched against/prime
expression andnumbers::prime
function is called.
Now lets show the final touch - the describe
function of myapp
:
void describe() { response().out() << "<a href='" << url("/numbers")<< "'>Numbers</a><br>" << "<a href='" << url("/letters")<< "'>Letters</a><br>" << "<a href='" << url("/numbers/odd")<< "'>Odd Numbers</a><br>"; }
URL("/numbers") - relates to the default URL of the "numbers" sub-application, and similar for letters
. The URL("/numbers/odd`) refers to "odd" mapping of the "numbers" sub-application. Let's see how the mapping is done in detail.
- A path
/numbers/odd
is split into "numbers" and "odd" tokens. - We search for "numbers" sub-application and find it.
- In
numbers
's sub-application mapper we search for "odd" mapping and find it. We construct all backward:
- "odd" -> "/odd"
- "/numbers{1}" + "/odd" -> "/numbers/odd"
- Root "/myapp" + "/numbers/odd" -> "/myapp/numbers/odd"
So we fully connect our main applications and sub-applications using some generic names.
Running
Finally let's add the main function
int main(int argc,char ** argv) { try { cppcms::service app(argc,argv); app.applications_pool().mount(cppcms::applications_factory<myapp>()); app.run(); } catch(std::exception const &e) { std::cerr<<e.what()<<std::endl; } }
Create the configuration file:
{ "service" : { "api" : "http", "port" : 8080 }, "http" : { "script" : "/myapp" } }
Build and run as:
./myapp -c config.js
And hit the URL: http://localhost:8080/myapp