Dispatcher
Overview
Routing requests is one of the most important aspects in each web service. Two primary reasons, having access control to the server content and providing user-friendly URLs.
e_dispatcher has both of them.
In order to provide URL mapping - the view/controller uses the dispatcher configuration file (dispatch.conf). This file (placed within the ../config directory) is read during the start of the application.
After making any change inside dispatch.conf, or configuration files included in it, you will need to force the dispatcher to reload its routing table. It can be accomplished by typing:
e_dispatcher:reinstall().
There are two subdirectories for pages, ../docroot and ../templates.
The ../docroot directory is public. By public, it can be accessible directly from the browser without any interaction with the framework, apart from the dispatcher.
In short, this means the whole of the ../docroot contents can be downloaded at once, depending on your web server configuration. If you allow the disk directories to be displayed, then many tools, like cURL, can download the contents of ../docroot with one command. Depending on your situation, this may be quite advantageous.
One immediate advantage is placing images, video, CSS, Javascript, etc. within the ../docroot directory as all of these items have no need to be evaluated at all. They are simply used as is. Any other files that meet the 'serve-as-is' qualification can be placed in the ../docroot directory as well. Additionally, if you do not want any of the Erlang web wpart(s) to be expanded, they too can be placed in ../docroot and the contents will be served without any translation.
If file security is a concern, you may add directories below ../docroot and they can be secured through dispatcher configuration.
../templates is used for static pages that use a template and for dynamic pages.
Regular Expressions
Erlang Web uses the standard Erlang/OTP 're' module for regular expression evaluation. A summary of 'Regexp' is below or a detailed description of the syntax and semantics of the meta-characters can be obtained at:
http://www.erlang.org/doc/man/re.html#sect2
The rules you create in the dispatcher deal with URLs and NOT filenames. One thing to keep in mind is that rules start with a / as per the HTTP RFC specification. Every entry in dispatch.conf that anchors the Regexp at the beginning must start with / and anything else is incorrect.
Note that since the 1.3 release, e_dispatcher is also capable of reverse dispatching.
Dispatching types
There are two basic types of dispatching: static and dynamic.
Static dispatching
Static dispatching has been designed for accessing the content which doesn’t need to be generated for a request (which is static). Files can be served directly as they are (examples include media, images and CSS). All without inspecting the content and/or changing the access path.
Or you can point to the template which needs expanding.
In both of these cases the controller is not accessed at all. You may not server dynamic content from within a static page. When you define the static entry in the dispatcher, {static,Regexp,Target), you must know the target filename for the request, and whether it is a template or located in ../docroot.
Dynamic dispatching
This involves the controller handling the URL requests. The URL the client enters into their browser is sent to the controller function. This allows content to be created on demand. In other word, perform access control, retrieve data from a database, create a web page, and validate input data would be examples of dynamic content. In short, the page could change with each client request. This includes redirecting the client to a different URL or using a different template.
Flow Control
The evaluation order of the dispatcher entries is as follows:
The ../docroot content is served (all of the files that point to the 'enoent' atom, like images, CSS, Javascript, etc.) More on 'enoent' atom later.
- Dynamic entries are matched with their respective URLs.
- Static routes are matched.
- If no pattern has been found, a 404 error will be displayed.
dispatch.conf
The dispatch.conf file is placed under the ../config directory and is the one of the must-have files in your system. It consists of Erlang tuples which tell the dispatcher how to handle the incoming requests.
Each entry contains a regular expression element - the dispatcher checks if it matches the requested URL.
The first matching entry will be used to handle the requested URL.
The following types of entries are allowed inside the dispatch.conf file:
{static, Regexp, File}
Statically dispatches the request to File. File should be placed in the ../templates directory. If there is no file with the given name, the server tries to find it within the ../docroot folder.
To make file serving faster, you can set File to enoent atom - it means the server should not look for it in the ../templates directory, but instead go directly to the ../docroot folder. .
{dynamic, Regexp, {Module, Function}}
Module:Function will be called in order to satisfy the request.
{alias, Regexp, NewURL}
The clause has been introduced in the 1.3 release. Its meaning is the same as RedirectMatch clause in the Apache server configuration file: If the incoming request's URL matches the Regexp the rule for the NewURL will be triggered. It is possible to use the named subpatterns mechanism in order to replace some parts of the NewURL with desired text parts from the incoming URL.
{dynamic, delegate, Regexp, File}
To make the dispatch.conf file clearer, easier to read and maintain we can use delegate entry. At first dispatcher reads the File and suffixes it with Regexp all Regexps in entries in the read file.
This kind of entry does not have any practical meaning - it only helps to structure the application. The same effect can be achieved by explicitly placing all of the entries from File in the original one (with suffixed Regexp element).
Yes, there is a trailing period “.” at the end of each entry and it is needed.
Regular Expression Meta Character Summary
\ - general escape character with several uses
^ - assert start of string (or line, in multiline mode)
$ - assert end of string (or line, in multiline mode)
. - match any character except newline (by default)
[ - start character class definition
| - start of alternative branch
( - start subpattern
) - end subpattern
? - extends the meaning of (, also 0 or 1 quantifier, also quantifier minimizer
* - 0 or more quantifier
+ - 1 or more quantifier, also "possessive quantifier"
{ - start min/max quantifier
Regular Expressions within dispatch.conf
While this is not intended to be a tutorial on regular expressions (Regexp), there is enough information here to get you grounded in the use of dispatch.conf and how it applies to you.
{static, Regexp, File}.
{static, Regexp, enoent}.
Examining each part: {static, Regexp, File}.
static – This file will not be evaluated by the dispatcher.
Regexp – This will be used to match the requested URL with the correct file.
File - The name of the target file. This file will be located in the /templates subdirectory. If the file is not found in the ../templates directory, the dispatcher will look in the ../docroot subdirectory. To speed file serving, substitute enoent for a filename and searching in ../templates will be skipped and the search will begin in ../docroot automatically.
Examples
This following entry could be used to serve a file with the name of index:
{static, index, index.html}.
As a first effort, this will work. It will also match everything that has "index" keyword inside, such as:
/index.html
/my_trip/photos/see_my_index.jpg
/js/indexer.js
and any other file that contains the character sequence “index”. The first file that is found will be the one that is served to the client.
This may appear confusing, as index.html was specified as the file to be served. But without placing limits on the search, any filename that has index within it and any extension qualifies.
By adding meta characters you are able to narrow the scope of the matching files.
Using the above listed meta characters, the following would be used to serve all *.gif files:
{static, "^/images/.*.gif$", enoent}.
static identifies the page as being static, that is not needing interpretation
the ^ forces the next character as being the first valid character.
the /images/ is simply the path being used and only from /images/
the first . means to match any character a except newline
the * indicates to match none or more characters
the .gif is the filename extension
the $ means the previous character is the final character that is valid
and finally enoent tells Erlang-web to retrieve the file from /docroot.
In summary, Erlang-web will go to ../docroot/images and fetch any filename.gif as needed.
To return to the index.html example above. By using the following meta characters, you can narrow your search to one specific file:
{static, "^index.html$", index.html}.
Named subpatterns
Named subpatterns is the mechanism used for extracting certain information from an input string. Using regular expressions, we are able to specify the semantics of the incoming URL and get its value. Moreover, the named subpatterns allows us to do a simple syntax validation.
The syntax is the same as in dynamic dispatcher rules, but the regular expression is modified a bit. If we want to bind some part of the input string to the given name, we should use the following syntax:
"HeadOfRegexp(?<NameOfThePattern>RegexpPart)TailOfRegexp"
When the whole regular expression matches the requested URL, it will extract its selected part and tag it with the specified name. The list of extracted values will be passed to the first function on dataflow list (if we are not using the named subpatterns mechanisms we will get the empty list there). The passed list will have format:
[{name_of_the_pattern, Value} | ...]For example, when we run a blog and we want to display our post by entering the address /show/post/Post_no, we can do it using named subpatterns:
{dynamic, "^/blog/post/(?<post_no>[0-9]+)$", {blog, display_post}}.When the incoming URL, let’s say /blog/post/23, could be described by the regular expression: /blog/post/[0-9]+$, the part responsible for the post number will be extracted from it. The result of the named subpatterns will be a property list:
[{post_no, "23"}]and it will be passed to the first function on the dataflow list.
We can place more than one named subpatterns in the regular expressions, for example:
{dynamic, "^/blog/post/(?<post_no>[0-9]+)/comment/(?<comment_title>.+)$}.for /blog/post/5/comment/first_comment URL, dispatcher will pass:
[{post_no, "5"}, {comment_title, "first_comment"}]to the first function on the dataflow list.
Skipping the dispatcher
Sometimes our service provides a very simple API and we will not need support from the dispatcher. Moreover, re module is not as fast as a Perl equivalent, so for efficiency reasons we will decide that we want uglier URL’s instead of many regular expression matches. One last reason for providing the dispatcher omission functionality is to keep backward compatibility with the prior version of Erlang Web.
If we want to skip the dispatcher, we must prefix the URL with app/. The meaning is as follows:
http://example.org/app/Module/Function/View.part1/View.part2/.../View.partN
The call path is split with / and the Module:Function is called (of course following the dataflow rules). In case Module:Function call returns the template atom, the View will be expanded (path to the template will be created by joining the View.partN).
Example
This is a simple example of nested dispatcher configuration files:
dispatch.conf
1 {dynamic, "^[/]*$", {main, home}}.
2 {dynamic, "^/index.html$", {main, home}}.
3
4 {dynamic, delegate, "^/user", "config/dispatcher/user.conf"}.
5
6 {static, "^/about$", "about.html"}.
7 {static, "^style.css$", enoent}.
and the included file:
user.conf
1 {dynamic, "/create$", {users, create}}.
2 {dynamic, "/delete$", {users, delete}}.
3
4 {static, "/welcome$", "users/welcome.html"}.
The following services will be accessible on corresponding URLs:
/ ⇒ function call main:home
index.html ⇒ function call main:home
style.css ⇒ direct access to style.css file in docroot directory
about ⇒ expanding template about.html
user/create ⇒ function call users:create
user/delete ⇒ function call users:delete
user/welcome ⇒ expanding template users/welcome.html
Example
The following example shows the alias clause possibilities: dispatch.conf
1 {dynamic, "^[/]*$", {main, home}}.
2 {alias, "^/index\.html$", "/"}.
3
4 {dynamic, "^/blog/read/(?<post_no>[0-9]+)/?$", {blog, read}}.
5 {alias, "^/blog/(?<post_no>[0-9]+)/?$", "/blog/read/(?<post_no>)"}.
The configuration above enables two basic operations:
requests which URLs contain only slashes (/) will be handled by main:home function
if the URL exactly matches "index.html", it will be handled the same as "/"
This case is rather simple and gives us only a little advantage - let's take a look at the second pair of rules.
The configuration above tells:
if the request has the URL that matches the "^/blog/read/(?<post_no>[0-9]+)/?$" regular expression, it will be handled by blog:read function and the [{post_no, No}] will be passed to the controller
but if the request has the URL that matches the "^/blog/(?<post_no>[0-9]+)/?$" - the request's URL should be replaced with "/blog/read/(?<post_no>)" and handled once again by the dispatcher.
We can see that all URLs having form "/blog/N" where N is an integer are equivalent with "/blog/read/N".
For example those two URLs will give us the same result: "/blog/5" and "/blog/read/5".
