URL routes
There may be situations where the desired URL format does not satisfy the assumptions behind Woof!'s default URL mapping. For example, you may want the URL
http://www.mycompany.com/support/ticket/display/123
to display the page corresponding to support ticket #123. This URL cannot be mapped to the appropriate controller by the default URL mapping since the mapping algorithm expects the action method to be the last component in the URL. In the above case the last component is actually the ticket number. Woof! allows such URL's to be handled through the definition of URL routes. Woof! will first check these route definitions for a match before falling back to the default mapping algorithm.
Route definition file
Woof! reads the URL route definitions from the file
routes.cfg in the
config subdirectory. This file is
is expected to contain Tcl code and is executed in a temporary
safe Tcl interpreter. The file may contain any Tcl code compatible
with safe interpreters. A route is defined through the
curl
command which has the following format:
curl CURL ACTIONS PURL
An example of a route is
curl /finance/companies {profile quote} symbol
This route will match application URL paths of the form
/finance/companies/quote/csco or
/finance/companies/profile/BRK.A and
invoke the quote
or profile
action in the controller
finance::Companies
passing it a parameter symbol
containing csco
and BRK.A
respectively. More
examples are given below.
The CURL
argument
specifies the relative URL for the controller and
may include a module path
but does not include Woof! application root.
The last component is the controller name and any prior
components specify the module. For example,
/x/y/z
in this component would be treated as
the controller class x::y::Z
or equivalently the
controller z
in module x y
.
ACTIONS
specifies the action methods for which the
definition is applicable. This may be a list of action names,
an empty list which
indicates the definition applies to all actions, or a string beginning
with implicit:
(for
example, implicit:display
.
In this last case, the URL is treated as not having
an action component and any remaining components after the controller
are matched against parameter definitions. The string after the
implicit:
prefix is treated as the action method to
invoke. An example is shown later.
PURL
is a URL path that defines additional parameters that
are supplied in the rest of the URL. Note these are not the explicit
parameters sent as part of a query or form post but rather additional
parameters that may be merged with them. Each component in this
PURL
should have the format
PARAMNAME:REGEXP:DEFAULT
where PARAMNAME
specifies the name of the parameter,
REGEXP
, if not empty, specifies a regular expression
that the URL component should match, and DEFAULT
is
the default value to be used if the URL
component is missing. DEFAULT
is actually passed through the
Tcl subst
command with -nocommands
options
hence variable
definitions and backslash sequences can be used. However, characters
that are special to Tcl will need to be escaped.
In addition, the PARAMNAME
field of the last path
component in PURL
may begin with a *
character in which case the corresponding
parameter is a list of all remaining URL
component values.
Note that any :
character in a default value should be
encoded using \u
Tcl escape sequences else it will be treated
as the start of the default value as opposed to be embedded in it.
Route matching
Routes defined through the curl
command are stored in
the order the corresponding commands are executed, which is
generally the order in which they occur in the file. Incoming
requests are matched against the routes in this order and the first
matching route is selected.
The matching algorithm first strips off the protocol scheme, host and port (if any) from the URL. The URL root of the Woof! application is also removed. The rest of the URL is then matched against each route definition with the first matching definition being selected.
For a match to succeed, all three portions of the definition - the controller URL, the action method name, and URL path embedded parameters (as opposed to query parameters) - must match.
Examples
These examples assume the host and application root URL http://www.mycompany.com/support/ is stripped off (support being the application root) leaving behind a relative URL that is matched against routes.
Route: simple route
curl /ticket index
The relative URL /ticket/index
would successfully match against this route and presumably show a list
of support tickets.
The action index
in class
Ticket
would be invoked. Note the URL
/ticket/index/123 does not match
this route as it has an additional URL component. In fact, this
route definition is redundant since the
default URL mapping behaviour
will give the same result.
Route: using parameters
curl /ticket display id
The relative URL /ticket/display/123
would successfully match against this route.
The action display
in class
Ticket
would be invoked. The parameter with name
id
with a value of 123
would be passed to
the method using the standard parameter passing mechanism described in the
Implementing actions chapter.
Route: multiple actions
curl /ticket {display edit} id
This is similar to the previous route except that two actions are listed so both /ticket/display/123 and /ticket/edit/123 will match.
Route: matching on parameter syntax
curl /ticket display {id:[[:digit:]]+:}
The problem with the route in the previous examples was that
although /ticket/display/123
would successfully match, so would the URL
/ticket/display/abc, something that
is probably undesired. Adding a regular expression to the parameter
definition ensures that only numeric strings will match the parameter
component for id
. The URL
/ticket/display/abc would therefore
not match the route.
The relative URL /ticket/display would also not match the route definition since no parameter is specified.
Route: using default parameter values
curl /ticket display {id:[[:digit:]]+:1}
Route: using implicit actions
curl /ticket implicit:display {id:[[:digit:]]+:}
You may also choose the implicit
action method
selection mechanism so that the display
component is
not required in the URL. So with the route definition in this example,
the relative URL
/ticket/123 would result in
same invocation of the display
action method as above.
Note that if you want URL's of the form shown in the previous examples
as well, both route definitions would need to be in place as this route
will not match the URL's in the previous example.
Route: parameter with list values
curl /ticket display {*id:[[:digit:]]+:}
Prefixing a parameter with the *
character causes
the parameter to collect the entire remaining portion of the URL as a
list value. So in this example, relative URL's such as
/ticket/display/12 and
/ticket/display/12/34/56
would both be accepted, with the id
parameter set to
lists {12}
and {12 34 56}
respectively.
Example: using Tcl code in route definition files
set year_regex {[[:digit:]]{4}} set month_regex {[[:digit:]]{1,2}} set day_regex {[[:digit:]]{1,2}} set date_params year:${year_regex}:/month:${month_regex}:/day:${day_regex}: foreach controller {blog article} { curl "/$controller" implicit:display $date_params }
This Tcl fragment in a route definition file would set up two routes
which would match URL's of the form
/blog/2009/1/21 or
/article/2009/1/21 and invoke the
display
action on the Blog
or Article
controllers respectively.