Overlapping routes priority

Hi, I have a site that was previously written with tornado and am porting to sanic (sanic is much faster in my testing :slight_smile: ) but having problems with the router.

I have one handler that attempts to load a page from the database based on the slug and a few other hard coded routes like so:

 app.add_route(uri=r"/contact/", handler=contact_view, name="contact")
 app.add_route(uri=r"/<slug:path>", handler=page_view, name="page")

Since the page_view route matches the "/contact/" url these routes are overlapping. With tornado it would just take the first rule matched but with sanic I cannot figure out what it is doing. It seems to work with some routes I have but not others.

I can manually re-route stuff in the page_view handler, but is there a better way to do it without putting the page_view under another path? Like a parameter to sort routes or prioritize one over another in this case?

The Sanic router is a bit more sophisticated. Basically it analyzes and organizes your routes to avoid those issues. If there is a potential conflict, the server will not even start and throw an error at startup.

In your case, any “path” route gets lowest priority. Just about every other route will be examined first. It does kot actually do it route by route. Instead it orders route possibilities by three main categories:

  1. “static” routes (not to be confused with routes handling static content, but routes that have no dynamic path params)
  2. “dynamic” routes
  3. expandable routes

First, when matching a route, the router looks to see if there’s an explicit route that matches precisely. No path params.

Second, failing that, it looks through segment by segment in a big tree to try and get to where it’s going. If it cannot journey through this tree and comes to an unknown segment, it jumps to step three.

Third, any expandable routes (a “path” param type or a regex pattern that allows a slash) are attempted in order. This is the only time insertion order would matter. If you have multiple “path” routes.

So, in your example, The contact route is the first type of route. It has no params and will be tried up front with every other static route in a O(1) operation.

If you were trying to navigate to /foo/bar, it would first determine that this route is not explicitly created. Then, it would look at foo and determine that there is no dynamic route with foo at segment 0. Finally, it would find the path expansion, do a regex match, which would yield the path route.

Did this answer the question?

1 Like

Thank you for the detailed explanation.

After playing with it more it was working with /contact but not /contact/ so I just had to remove the trailing “/” on a few of my menu links in the database.

Edit: Using strict_slashes=True made it so I didn’t have to change anything.