URI routing is the process of taking the requested URI and deciding which application handler will handle the current request. For this, we initialize the WSGIApplication defining a list of routes: each route analyses the current request and, if it matches certain criterias, returns the handler and optional variables extracted from the URI.
webapp2 offers a powerful and extensible system to match and build URIs, which is explained in details in this section.
The simplest form of URI route in webapp2 is a tuple (regex, handler), where regex is a regular expression to match the requested URI path and handler is a callable to handle the request. This routing mechanism is fully compatible with App Engine’s webapp framework.
This is how it works: a list of routes is registered in the WSGI application. When the application receives a request, it tries to match each one in order until one matches, and then call the corresponding handler. Here, for example, we define three handlers and register three routes that point to those handlers:
class HomeHandler(webapp2.RequestHandler): def get(self): self.response.write('This is the HomeHandler.') class ProductListHandler(webapp2.RequestHandler): def get(self): self.response.write('This is the ProductListHandler.') class ProductHandler(webapp2.RequestHandler): def get(self, product_id): self.response.write('This is the ProductHandler. ' 'The product id is %s' % product_id) app = webapp2.WSGIApplication([ (r'/', HomeHandler), (r'/products', ProductListHandler), (r'/products/(\d+)', ProductHandler), ])
When a request comes in, the application will match the request path to find the corresponding handler. If no route matches, an HTTPException is raised with status code 404, and the WSGI application can handle it accordingly (see Exception handling).
The regex part is an ordinary regular expression (see the re module) that can define groups inside parentheses. The matched group values are passed to the handler as positional arguments. In the example above, the last route defines a group, so the handler will receive the matched value when the route matches (one or more digits in this case).
Simple routes are easy to use and enough for a lot of cases but don’t support keyword arguments, URI building, domain and subdomain matching, automatic redirection and other useful features. For this, webapp2 offers the extended routing mechanism that we’ll see next.
webapp2 introduces a routing mechanism that extends the webapp model to provide additional features:
And several other features and benefits.
The concept is similar to the simple routes we saw before, but instead of a tuple (regex, handler), we define each route using the class webapp2.Route. Let’s remake our previous routes using it:
app = webapp2.WSGIApplication([ webapp2.Route(r'/', handler=HomeHandler, name='home'), webapp2.Route(r'/products', handler=ProductListHandler, name='product-list'), webapp2.Route(r'/products/<product_id:\d+>', handler=ProductHandler, name='product'), ])
Check webapp2.Route.__init__() in the API reference for the parameters accepted by the Route constructor. We will explain some of them in details below.
The URL template defines the URL path to be matched. It can have regular expressions for variables using the syntax <name:regex>; everything outside of <> is not interpreted as a regular expression to be matched. Both name and regex are optional, like in the examples below:
The same template can mix parts with name, regular expression or both.
The name, if defined, is used to build URLs for the route. When it is set, the value of the matched regular expression is passed as keyword argument to the handler. Otherwise it is passed as positional argument.
If only the name is set, it will match anything except a slash. So these routes are equivalent:
Route('/<user_id>/settings', handler=SettingsHandler, name='user-settings') Route('/<user_id:[^/]+>/settings', handler=SettingsHandler, name='user-settings')
The handler only receives *args if no named variables are set. Otherwise, the handler only receives **kwargs. This allows you to set regular expressions that are not captured: just mix named and unnamed variables and the handler will only receive the named ones.
One additional feature compared to webapp is that the handler can also be defined as a string in dotted notation to be lazily imported when needed.
This is useful to avoid loading all modules when the app is initialized: we can define handlers in different modules without needing to import all of them to initialize the app. This is not only convenient but also speeds up the application startup.
The string must contain the package or module name and the name of the handler (a class or function name). Our previous example could be rewritten using strings instead of handler classes and splitting our handlers in two files, handlers.py and products.py:
app = webapp2.WSGIApplication([ (r'/', 'handlers.HomeHandler'), (r'/products', 'products.ProductListHandler'), (r'/products/(\d+)', 'products.ProductHandler'), ])
In the first time that one of these routes matches, the handlers will be automatically imported by the routing system.
A parameter handler_method can define the method of the handler that will be called, if handler is a class. If not defined, the default behavior is to translate the HTTP method to a handler method, as explained in Request handlers. For example:
webapp2.Route(r'/products', handler='handlers.ProductsHandler', name='products-list', handler_method='list_products')
Alternatively, the handler method can be defined in the handler string, separated by a colon. This is equivalent to the previous example:
webapp2.Route(r'/products', handler='handlers.ProductsHandler:list_products', name='products-list')
If needed, the route can define a sequence of allowed HTTP methods. Only if the request method is in that list or tuple the route will match. If the method is not allowed, an HTTPMethodNotAllowed exception is raised with status code 405. For example:
webapp2.Route(r'/products', handler='handlers.ProductsHandler', name='products-list', methods=['GET'])
This is useful when using functions as handlers, or alternative handlers that don’t translate the HTTP method to the handler method like the default webapp2.RequestHandler does.
Like with HTTP methods, you can specify the URI schemes allowed for a route, if needed. This is useful if some URIs must be accessed using ‘http’ or ‘https’ only. For this, set the schemes parameter when defining a route:
webapp2.Route(r'/products', handler='handlers.ProductsHandler', name='products-list', schemes=['https'])
The above route will only match if the URI scheme is ‘https’.
The routing system can also handle domain and subdomain matching. This is done using a special route class provided in the webapp2_extras.routes module: the webapp2_extras.routes.DomainRoute. It is initialized with a pattern to match the current server name and a list of nested webapp2.Route instances that will only be tested if the domain or subdomain matches.
For example, to restrict routes to a subdomain of the appspot domain:
import webapp2 from webapp2_extras import routes app = webapp2.WSGIApplication([ routes.DomainRoute('<subdomain>.app-id.appspot.com', [ webapp2.Route('/', handler=SubdomainHomeHandler, name='subdomain-home'), ]), webapp2.Route('/', handler=HomeHandler, name='home'), ])
In the example above, we define a template '<subdomain>.app-id.appspot.com' for the domain matching. When a request comes in, only if the request server name matches that pattern, the nested route will be tested. Otherwise the routing system will test the next route until one matches. So the first route with path / will only match when a subdomain of the app-id.appspot.com domain is accessed. Otherwise the second route with path / will be used.
The template follows the same syntax used by webapp2.Route and must define named groups if any value must be added to the match results. In the example above, an extra subdomain keyword is passed to the handler, but if the regex didn’t define any named groups, nothing would be added.
A common need is to set some routes for the main subdomain (www) and different routes for other submains. The webapp2 routing system can handle this easily.
To match only the www subdomain, simple set the domain template to a fixed value:
routes.DomainRoute('www.mydomain.com', [ webapp2.Route('/', handler=HomeHandler, name='home'), ])
To match any subdomain except the www subdomain, set a regular expression that excludes www:
routes.DomainRoute(r'<subdomain:(?!www\.)[^.]+>.mydomain.com', [ webapp2.Route('/', handler=HomeHandler, name='home'), ])
Any subdomain that matches and is not www will be passed as a parameter subdomain to the handler.
Similarly, you can restrict matches to the main appspot domain or a www domain from a custom domain:
routes.DomainRoute(r'<:(app-id\.appspot\.com|www\.mydomain\.com)>', [ webapp2.Route('/', handler=HomeHandler, name='home'), ])
And then have a route that matches subdomains of the main appspot domain or from a custom domain, except www:
routes.DomainRoute(r'<subdomain:(?!www)[^.]+>.<:(app-id\.appspot\.com|mydomain\.com)>', [ webapp2.Route('/', handler=HomeHandler, name='home'), ])
The webapp2_extras.routes provides a class to wrap routes that start with a common path: the webapp2_extras.routes.PathPrefixRoute. The intention is to avoid repetition when defining routes.
For example, imagine we have these routes:
app = WSGIApplication([ Route('/users/<user:\w+>/', UserOverviewHandler, 'user-overview'), Route('/users/<user:\w+>/profile', UserProfileHandler, 'user-profile'), Route('/users/<user:\w+>/projects', UserProjectsHandler, 'user-projects'), ])
We could refactor them to reuse the common path prefix:
import webapp2 from webapp2_extras import routes app = WSGIApplication([ routes.PathPrefixRoute('/users/<user:\w+>', [ webapp2.Route('/', UserOverviewHandler, 'user-overview'), webapp2.Route('/profile', UserProfileHandler, 'user-profile'), webapp2.Route('/projects', UserProjectsHandler, 'user-projects'), ]), ])
This is not only convenient, but also performs better: the nested routes will only be tested if the path prefix matches.
The webapp2_extras.routes has other convenience classes that accept nested routes with a common attribute prefix:
Because our routes have a name, we can use the routing system to build URIs whenever we need to reference those resources inside the application. This is done using the function webapp2.uri_for() or the method webapp2.RequestHandler.uri_for() inside a handler, or calling webapp2.Router.build() directly (a Router instance is set as an attribute router in the WSGI application).
For example, if you have these routes defined for the application:
app = webapp2.WSGIApplication([ webapp2.Route('/', handler='handlers.HomeHandler', name='home'), webapp2.Route('/wiki', handler=WikiHandler, name='wiki'), webapp2.Route('/wiki/<page>', handler=WikiHandler, name='wiki-page'), ])
Here are some examples of how to generate URIs for them:
# / uri = uri_for('home') # http://localhost:8080/ uri = uri_for('home', _full=True) # /wiki uri = uri_for('wiki') # http://localhost:8080/wiki uri = uri_for('wiki', _full=True) # http://localhost:8080/wiki#my-heading uri = uri_for('wiki', _full=True, _fragment='my-heading') # /wiki/my-first-page uri = uri_for('wiki-page', page='my-first-page') # /wiki/my-first-page?format=atom uri = uri_for('wiki-page', page='my-first-page', format='atom')
Variables are passed as positional or keyword arguments and are required if the route defines them. Keyword arguments that are not present in the route are added to the URI as a query string.
Also, when calling uri_for(), a few keywords have special meaning:
Check webapp2.Router.build() in the API reference for a complete explanation of the parameters used to build URIs.
The parameters from the matched route are set as attributes of the request object when a route matches. They are request.route_args, for positional arguments, and request.route_kwargs, for keyword arguments.
The matched route object is also available as request.route.