<!--toc--> |
|
## General |
|
The basic approach of mapping a URL to a member function of out class works for small applications but become problematic for complex systems. |
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 to create a hierarchy of applications that work as a single unit connect between between them using the `url_mapper` and `url_dispatcher` classes. |
|
Before you read this tutorial, please read [URL Dispatching and Mapping](/wikipp/en/page/cppcms_1x_tut_url_mapping) tutorial. |
|
## Example |
|
### Hierarchy |
|
We would create a very simple example of "educational" application that consists of two sub-applications: |
|
- myapp - main application |
- numbers - display different numbers: |
- all |
- odd |
- even |
- prime |
- letters - display different letter: |
- all |
- capital |
- small |
|
We would create 3 classes connected between them where |
`myapp` would be the topmost class in the hierarchy and `numbers` and `letters` would be children 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 when the `numbers` application does not even know what is that |
- `url("/letters")` - points to the default location of the sub-application letters starting from the root. |
- `url(".")` - points to the default handling for current application |
- `url("odd")`, `url("even")`, `url("prime")` - point |
to current applications different parts. |
|
|
Now we create the same 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 mapper according |
the the patter "/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 child's main function for further dispatching. |
|
The same done for the `letters` and `myapp` own member functions. |
|
Now lets analyze how an HTTP request `GET /myapp/numbers/prime` is parsed. |
|
1. Web server splits `/myapp/numbers/prime` to `/myapp` as `SCRIPT_NAME` and `/numbers/prime` as `PATH_INFO` |
2. `myapp` matches `/numbers/prime` against `/numbers(/(.*))?` expression and extracts `/prime` as |
subexpression that is passed to `numbers`'s main function. |
3. `/prime` is matched against `/prime` expression and `numbers::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>"; |
} |
|
The URL "/numbers" - relates to the default URL of "numbers" sub-application, same for `letters`. The "/numbers/odd` refer to "odd" mapping of "numbers" sub-application. Let's see how mapping is done in details. |
|
1. A path `/numbers/odd` is split into "numbers" and "odd" tokens. |
2. We search for "numbers" sub-application and find it. |
3. In `numbers`'s sub-application mapper we search for "odd" mapping and find it. |
4. 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 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 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> |
|
|
|
|