Overview
Introduction
The org.restlet.routing package contains classes related to call routing.
Another advantage of the Restlet Framework is the built-in support for cool URIs. A good description of the importance of proper URI design is given by Jacob Nielsen in his AlertBox. This document aims at showing the several configuration options of the routing process. From a high level point of view, routing is mainly based on the Router, Route, Template and Variable classes.
A Router is a Restlet that helps to associate a URI to the Restlet or
the Resource that will handle all requests made to this URI.
The association of a URI and the target Restlet or Resource is
personified by an instance of the Route class. In general, people wants
to express generic associations such as: all URIs
“/my/base/uri/{variable part}” target this Restlet or Resource. Hence,
the Restlet framework allows to define routes based on the URI template
specification.
URI Templates allows to defines pattern of URIs that contain embedded variables. These concepts have been formalised in the Restlet famework under the form of Template and Variable classes.
Here is a sample code illustrating the default usage of a Router:
// Create a router Restlet that defines routes.
Router router = new Router(getContext());
// Defines a route for the resource "list of items"
router.attach("/items", ItemsResource.class);
// Defines a route for the resource "item"
router.attach("/items/{itemName}", ItemResource.class);
It declares two routes, the first one associates the URI “/items” to the Resource “ItemsResource” and the second one “/items/itemName” to the Resource “ItemResource”. The latter defines a variable called “itemName” which can be retrieved by the Resource.
Please note that a route attaches instances of a Restlet subclass whereas resources are attached via their class. The reason is that Resources have been designed to handle a single request, thus instances are generated at run-time via their class.
Default configuration
This chapter covers either the default behavior of instances of the Router, Route, Template and Variable classes or the behavior that applies by default during the process of routing a URI.
Matching mode
The default behavior of a Router is to match the whole request URI (actually the remaining part, left by previous Routers) with the attached URI patterns. If the request URI indeed is equal to one of the URI patterns, the target Resource or Restlet is invoked. The way the matching is done by default is based on the Template#MODE_EQUALS constant, meaning that the URI template must exactly match the remaining part. This is a strict mode that forbids subsequent routing.
This default routing mode can be changed by calling the Router#setDefaultMatchingMode() method with the Template#MODE_STARTS_WITH constant meaning that if the URI template associated to the route matches the start of the target resource URI, then it is accepted. This is a flexible mode that allows a hierarchy of routers to be used.
Here is a sample code:
router.setMatchingMode(Template.MODE_STARTS_WITH);
Routing and queries
With the default matching mode to Template#MODE_EQUALS, your Router won’t accept URI with query strings going to this Resource. To solve this, you can either indicate at the router level, that the matching process does not take into account the query part of the reference:
router.setDefaultMatchQuery(false);
Or you can explicitly allow the query part in your URI template:
TemplateRoute route = router.attach("/users/{user}?{query}", UserResource.class);
route.setMatchingQuery(false);
Routing mode
In addition to the matching mode, a Router supports several routing modes, implementing various algorithms:
- Best match
- First match (default since Restlet 2.0)
- Last match
- Random match
- Round robin
- Custom
By default, when considering a URI to route, a Router computes an affinity score for each declared route then choose the one that have the best affinity score. You can update this behavior by setting the routing mode property with one of the listed modes declared above.
router.setRoutingMode(Router.FIRST);
Matching of template variables
As said in the introduction, the routing feature relies on the declaration of templates of URI. In the sample code below, the URI template declares one variable called “user”:
TemplateRoute route = router.attach("/users/{user}", UserResource.class);
At run-time, the value of this variable is put into the attributes of the request:
this.userId = (String) getRequest().getAttributes().get("user");
By default a variable is meant to match a URI segment as defined in the URI specification document. That is to say a sequence of characters between two “/”.
A variable holds several other attributes:
- defaultValue: default value to use if the key couldn’t be found in the model (“” by default)..
- required: indicates if the variable is required or optional (true by default).
- fixed: indicates if the value is fixed, in which case, the default property is always used (false by default).
- decodedOnParse: indicates if the parsed value must be decoded (false by default).
Tuning the routing
Sometimes the same matching mode can’t be used for all routes. For example one delegates the routing to a child router while others routes are resources directly processing the request. In this case, it is possible to specify a different routing mode for each route using the Route#setMatchingMode() method. The Route instance is returned by the Router#attach() methods.
Matching mode
Regarding the matching mode, the default behavior of a Router can be customized for a single route by updating its underlying template instance as follow:
TemplateRoute route = router.attach("/users/{user}", UserResource.class);
route.getTemplate().setMatchingMode(Template.MODE_STARTS_WITH);
Routing and queries
If you want to route URIs according to some values located in the query
part of the URI, and since those values have no determined place within
the query, you cannot rely on the URI template based routing feature
which is more or less based on regular expressions.
In this case you have to define your own router (with the “MODE_CUSTOM”
routing mode). A sample implementation is available
here(application/zip, 3.6 kB).
Matching of template variables
By default, as said above, a template variable is meant to match a URI segment. Here is the list of all available type of variables. For additional details, see the Javadocs of the Variable class:
Value | Description |
---|---|
TYPE_ALL | Matches all characters. |
TYPE_ALPHA | Matches all alphabetical characters. |
TYPE_ALPHA_DIGIT | Matches all alphabetical and digital characters. |
TYPE_COMMENT | Matches any TEXT excluding “(“ and “)”. |
TYPE_COMMENT_ATTRIBUTE | Matches any TEXT inside a comment excluding “;”. |
TYPE_DIGIT | Matches all digital characters. |
TYPE_TOKEN | Matches any CHAR except CTLs or separators. |
TYPE_URI_ALL | Matches all URI characters. |
TYPE_URI_FRAGMENT | Matches URI fragment characters. |
TYPE_URI_PATH | Matches URI path characters (not the query or the fragment parts). |
TYPE_URI_QUERY | Matches URI query characters. |
TYPE_URI_SCHEME | Matches URI scheme characters. |
TYPE_URI_SEGMENT | Matches URI segment characters. |
TYPE_URI_UNRESERVED | Matches unreserved URI characters. |
TYPE_WORD | Matches all alphabetical and digital characters plus the underscore. |
Here is the way to change the type of a variable:
TemplateRoute route = router.attach("/items/{itemName}", ItemResource);
Map<String, Variable> routeVariables = route.getTemplate().getVariables();
routeVariables.put("itemName", new Variable(Variable.TYPE_URI_WORD));