Sei sulla pagina 1di 4

Detailed Description

class for mapping URLs - the opposite of dispatch


This class is useful for mapping between different page identifications that represent different classes/view and the
URL.
The URL mapping is done in hierarchy of applications where each application has its own name and they bundler into
single hierarchy. Each application in hierarchy can be referred by its key and location. It may have different "urls" for
mapping different values.
For example, we develop a content management system with following tools:

site

news
article
category

forums
topics
threads

users
management
registration
profile

Each node above is represented my cppcms::application and its children mounted to it. In order to access each URL
we use "file system" like convention:

"/" - the default site page


"/news" - the default news page
"/news/article" - the default article page
"/news/category/by_date" - the categories pages sorted by data
"/news/category/by interest" - the categories pages sorted by interest
"/news/category" - the default category pages

And so on.
Each application can be referred by its full path from root or by its relative path, so if we for example in article subapplication and we want to refer to "category" URL we would use something like
map(out(),"../category/by_interest",category_id);
In order to all this process work, each application should mount its children by some name, like:

site::site(cppcms::service &s) :
cppcms::application(s),
news_(s),
forums_(s),
users_(s)
{
add(news_);
mapper().mount("news","/news{1}",news_);
add(forums_);
mapper().mount("forums","/forums{1}",forums_);
...
You can also use cppcms::application::add and cppcms::application::attach that allows to provide mapping
for url_dispatcher and url_mapper in a single command like:

add(news_,
"news","/news{1}",

"/news((/.*)?)",1);
Which effectively the same as:

add(news_);
mapper().mount("news","/news{1}",news_);
dispatcher().mount("/news((/.*)?)",news_,1);
Such system allows using "url" tag in templates system easily as"

<a href="<% url "/news/article" using article id %>" >...</a>


Each mounted application may a default URL (something like index.html) which is mapped when mounted application is
referred. So for example there are may be following URLs:

"/news/article" or "/news/article/" - the default URL


"/news/article/preview" - preview unpublished article URL.

They can be defined in article class as following:

article::article(cppcms::service &s) : cppcms::application(s)


{
mapper().assign("/{1}"); // the default URL
dispatcher().assign("/(\\d+)",&article::display,this,1);
mapper().assign("preview","/{1}/preview"); // the preview URL
dispatcher().assign("/(\\d+)/preview",&article::preview,this,1);
}
Additional supported feature is "named" parameters which are usually set to some default value using set_value, they
are defined by special keywords between instead of numbers.
For example assign("article", "/article/{lang}/{1}") were "lang" is special value for language defined by
set_value("lang","en"), so mapping map(out(),"article",10) would create a URL "/article/en/10"
Sometimes it is useful to change such values there are two ways to do it:

Overloading keyword with different parameters number assign("article","/article/{1}/{2}") and then


calling map(out(),"article","ru",10) for URL like "/article/ru/10".
Using naming of parameters at mapping level by prepending comma separated keywords at the end of the
path line after ";" map(out(),"article;lang","ru",10) - which would effectively work like temporary calling
set_value("lang","ru") and then calling map(out(),"article",10). Unlike the previous case it also allows to do
such changes globally.
For example if "news" application mounts article using "/{lang}/{1}" then using such mapping would affect
top level URL that does not belong to specific application.

Practice:
Introduction
Now we would learn how to connect between different URLs and the application functions easily.
When
client
requests
specific
URL
from
the
HTTP
server
it
divides
it
into
several
parts
(CGI
variables): SCRIPT_NAME, PATH_INFO and QUERY_STRING. For example URL/foo/bar.php/test?x=10 is
separated into:

SCRIPT_NAME = /foo/bar.php
PATH_INFO = /test
QUERY_STRING = x=10

CppCMS web applications are not distributed into several scripts but rather written using a single FastCGI application that runs on
specific "script" url like "/myapp", So generally URLs would look like /myapp/page/10, when SCRIPT_NAME = "/myapp"
remains constant andPATH_INFO = /page/10 is changed according to user's needs.
So the mapping between URL and applications member functions are done by matching a regular
against PATH_INFO part of URL, even though this behavior can be changed by using mount_point class.

expression

This job is done by cppcms::url_dispatcher class.


The opposite of the dispatching is "mapping" when the names of the application parts are converted to a URL that can be given to the
user.

Code
Mapping
Lets rewrite our hello class:
Since we will be routing URLs, let's include the url_dispatcher.h and url_mapper.hheader file. Your include list
should look like:
1.
2.
3.
4.
5.
6.
7.
8.

#include
#include
#include
#include
#include
#include
#include
#include

<cppcms/application.h>
<cppcms/service.h>
<cppcms/http_response.h>
<cppcms/url_dispatcher.h>
<cppcms/url_mapper.h>
<cppcms/applications_pool.h>
<iostream>
<stdlib.h>

Now in our constructor, let's map some URLs to member functions:


1. hello(cppcms::service &srv) :
2.
cppcms::application(srv)
3. {
4.
dispatcher().assign("/number/(\\d+)",&hello::number,this,1);
5.
mapper().assign("number","/number/{1}");
6.
7.
dispatcher().assign("/smile",&hello::smile,this);
8.
mapper().assign("smile","/smile");
9.
10.
dispatcher().assign("",&hello::welcome,this);
11.
mapper().assign("");
12.
13.
mapper().root("/hello");
14. }

In the fourth line we connect the regular expression /number/(\d+) to the member function number of this instance that
receives std::string as parameter, and 1st captured subexpression is passed to it (note the parameter 1 that represents the 1st
subexpression as a parameter)
In this case the subexpression is nonempty string that contains digits.
In the 5th line we create a name for this part called "number" and we provide a URL formatting pattern /number/{1} - the
opposite of regular expression where {1} is a placeholder for the first parameter for formatting the appropriate URL.
In next lines 7-8 connect member function smile to its URL and create a mapping. Because this URL does not have any parameters
it does not have any placeholders.
The the line 10 we connect the empty URL to the welcome member function and in the line 11 we define default empty mapping
patter for the URL. Note, because we pass only one parameter to the assign() function, we define the default URL for the
application.
In the line 13 we define the "root" of the all URLs for the url_mapper class, in our case it should be identical the
the SCRIPT_NAME CGI variable.

Description
So when the web server receives requests that match a script name /hello it forwards them to the application:

/hello - welcome function would be called


/hello/smile - smile function would be called
/hello/number/10 - number function would be called and receive a string "10" as parameter.

Because the "routing table" between the URL and the application member functions is done we don't need to override
the cppcms::application::main member function as it does the all dispatching for us.

Actions
The "number" member function.

1.
2.
3.
4.
5.
6.

void number(std::string num)


{
int no = atoi(num.c_str());
response().out() << "The number is " << no << "<br/>\n";
response().out() << "<a href='" << url("/") << "'>Go back</a>";
}

Note, we using url("/") as abstract URL for the default application entry point - the / part describes the root application (our
single application) and the fact that nothing follows it shows that the default URL should be given - the mapping the
twe welcome member functions
In the same way we create the smile member function:
1.
2.
3.
4.
5.

void smile()
{
response().out() << ":-) <br/>\n";
response().out() << "<a href='" << url("/") << "'>Go back</a>";
}

Now we would write out welcome action:


1.
2.
3.
4.
5.
6.
7.
8.

void welcome()
{
response().out() <<
"<h1> Wellcome To Page with links </h1>\n"
"<a href='" << url("/number",1) << "'>1</a><br>\n"
"<a href='" << url("/number",15) << "'>15</a><br>\n"
"<a href='" << url("/smile") << "' >:-)</a><br>\n";
}

Note, we show three URLs to the different application parts: "number" - and we would pass a parameter 1 to
the cppcms::application::url function that would be rendered instead of the placeholder {1}, we do the same for
number 15 and pass a URL to "smile" part without parameters.
Lets describe in the example the actions that are taken for when we call url("/number",15):

First for "/" we go to the topmost application, in out case to this one.
Next we match "number" against the known pattern "number" that was mapped to"/number/{1}".
We
fetch
the
pattern
and
use
convert:/number/{1} to /number/15.

the

parameter

that

is

passed

to url function

and

Finally we perpend the root URL path "/hello" and get our final URL /hello/number/15.

Note: the parameters to url family of functions can be anything that can be written tostd::ostream - i.e. any object that
defined operator << with standard C++ streams.

Running The Example


Recompile it:
1.

c++ hello.cpp -lcppcms -o hello

And now for the exciting part, start your application:


1.

./hello -c config.js

Potrebbero piacerti anche