Resolution Algorithms
Routing is organized in a tree with a recursive matching system that is capable of handling quite complex rules for request processing. The Tree is built with nodes that contain selector and optional handler. Selectors are used to pick a route based on request. Every selector has quality, usually from 0.0 to 1.0, with one special case of "transparent quality" (-1.0). This quality is useful when you need to wrap your subtree in a routing block, but this block should not change routes priority. Example of it can be route("/")
block in the middle of a routing tree.
Example tree 1:
Here, the first subtree will match only /a
and the second will match any URL with one path segment, such as /a
, /b
, /any_other_path
. But for request GET /a
the first route will win, because it has a better quality. You can see more details about the routing resolution algorithm below.
Example tree 2:
Two show how resolution will work in this more complex scenario, we need to specify the routing resolution algorithm. It consists of two parts.
Traversing the tree to find matched routes:
Select root route's node as current entry
If the node has handlers and all path segments are used, add current node to successful matches
For every child:
If child's selector does not match or selector quality is not transparent and is less than the best child match quality, skip current child
Go to step 2 with child as node
If current child's quality is better than the best child's, update the best matched child
Picking the best match:
Collect all paths from the root of the tree to the successful matched nodes
From all paths, remove nodes with "transparent" quality
Choose paths with the highest qualities
If there are multiple path with the same quality, chose the first one
Actual implementation may differ in details for optimization reasons, but its result must be the same, as algorithm described here.
For the second example tree, routing resolution for request GET /a/b
will look like this:
Check root route
route("a")
-> success, q=1.0, traverse childrenCheck first child
route("b")
-> success, q=1.0, traverse childrenCheck first child
method(Get)
-> success, q=1.0. Route has handler, add it to successful matchesCheck second child
method(Post)
-> failure
Subtree has matches, update the best child match with current child
route("b")
, quality 1.0Check second child
route("/")
-> success, q=-1, traverse childrenCheck first child
route("*")
-> success, q=0.5. Route has handler, add it to successful matches
Check third child
route("{...}")
-> success, q=0.1. Quality is less than the best child quality, ignore subtree
Success matches are
/ a,q=1.0 / b,q=1.0 / :get,q=1.0
anda,q=1.0 / </>,q=-1.0 / <*>,q=0.5
Ignoring transparent qualities, matches are
/ a,q=1.0 / b,q=1.0 / :get,q=1.0
anda,q=1.0 / <*>,q=0.5
First result has higher quality for the second node, choose it.