Dynamic Routes in aiohttp: Flexible URL Handling
Dynamic routes in aiohttp allow you to create flexible URL patterns that can handle variable inputs.
In this tutorial, you’ll learn how to implement dynamic routes, from basic usage to advanced methods.
Basic Dynamic Routes
To create a dynamic route with a variable segment, use curly braces {} in your URL pattern:
from aiohttp import web async def greet(request): name = request.match_info['name'] return web.Response(text=f"Hello, {name}!") app = web.Application() app.router.add_get('/{name}', greet) web.run_app(app)
Output:
======== Running on http://0.0.0.0:8080 ======== (Press CTRL+C to quit)
When you access http://localhost:8080/Adam, you’ll see “Hello, Adam!” in your browser.
The {name} segment in the URL pattern captures any value provided in that position and makes it available in the request.match_info dictionary.
Regular Expression Routes
You can use regular expressions to define more complex URL patterns:
import re from aiohttp import web async def handle_product(request): product_code = request.match_info['code'] return web.Response(text=f"Product code: {product_code}") app = web.Application() app.router.add_get(r'/product/{code:[A-Z]{2}\d{4}}', handle_product) web.run_app(app)
Output:
======== Running on http://0.0.0.0:8080 ======== (Press CTRL+C to quit)
This route will match URLs like http://localhost:8080/product/AB1234.
The regular expression [A-Z]{2}\d{4} allows only valid product codes where the product code consists of two uppercase letters followed by four digits.
Wildcard Routes
To match any number of path segments, use the {tail:.*} syntax:
from aiohttp import web async def catch_all(request): path = request.match_info['tail'] return web.Response(text=f"Caught path: /{path}") app = web.Application() app.router.add_get('/{tail:.*}', catch_all) web.run_app(app)
Output:
======== Running on http://0.0.0.0:8080 ======== (Press CTRL+C to quit)
This route will match any URL path, capturing the entire path after the domain.
For example, http://localhost:8080/any/path/here will display “Caught path: /any/path/here”.
Route Prioritization and Order
In aiohttp
, the order in which routes are added to the application matters because the router processes them sequentially.
This means that more specific routes should be added before more general ones to ensure they are matched correctly.
from aiohttp import web async def handle_root(request): return web.Response(text="This is the root handler") async def handle_user(request): user_id = request.match_info.get('user_id', "Anonymous") return web.Response(text=f"User ID: {user_id}") async def handle_wildcard(request): return web.Response(text="This is the wildcard handler") app = web.Application() # Add the more specific route first app.router.add_get('/user/{user_id}', handle_user) # Add a more general route later app.router.add_get('/user/{tail:.*}', handle_wildcard) app.router.add_get('/', handle_root) web.run_app(app)
Specific Route: The route /user/{user_id}
is added first. This route is more specific because it matches a URL pattern with a specific structure, expecting a user_id
.
Wildcard Route: The route /user/{tail:.*}
is added after the specific route.
This route uses a wildcard to match any path that starts with /user/
and capture the rest of the path in the tail
variable.
If this route were added before the specific route, it would match all /user/...
requests and will prevent you from accessing the specific route.
URL Query Parameters with Dynamic Routes
Combine path variables and query parameters
You can use both path variables and query parameters in your routes:
from aiohttp import web async def search_products(request): category = request.match_info['category'] query = request.query.get('q', '') return web.Response(text=f"Search in category '{category}' for '{query}'") app = web.Application() app.router.add_get('/{category}/search', search_products) web.run_app(app)
Output:
======== Running on http://0.0.0.0:8080 ======== (Press CTRL+C to quit)
This route will handle URLs like http://localhost:8080/electronics/search?q=laptop, where “electronics” is the category from the path, and “laptop” is the search query from the query parameter.
You can handle both optional and required query parameters:
from aiohttp import web async def filter_products(request): category = request.match_info['category'] min_price = request.query.get('min_price') max_price = request.query.get('max_price') if min_price is None or max_price is None: return web.Response(text="Both min_price and max_price are required", status=400) return web.Response(text=f"Filtering {category} products between ${min_price} and ${max_price}") app = web.Application() app.router.add_get('/{category}/filter', filter_products) web.run_app(app)
Output:
======== Running on http://0.0.0.0:8080 ======== (Press CTRL+C to quit)
This route requires both min_price and max_price query parameters.
A request to http://localhost:8080/electronics/filter?min_price=100&max_price=500 will work, but if you remove any parameter, it will result in a 400 Bad Request response.
Internationalization with Dynamic Routes
Localizing URL patterns
You can create localized URL patterns for different languages:
from aiohttp import web async def welcome(request): lang = request.match_info['lang'] name = request.match_info['name'] greetings = { 'en': 'Hello', 'es': 'Hola', 'fr': 'Bonjour' } greeting = greetings.get(lang, greetings['en']) return web.Response(text=f"{greeting}, {name}!") app = web.Application() app.router.add_get('/{lang:(en|es|fr)}/{name}', welcome) web.run_app(app)
Output:
======== Running on http://0.0.0.0:8080 ======== (Press CTRL+C to quit)
This route supports localized greetings for English, Spanish, and French.
If you access http://localhost:8080/es/Maria, it will return “Hola, Maria!”, while http://localhost:8080/fr/Pierre will return “Bonjour, Pierre!”.
Language-specific Routing
You can create separate routes for different languages:
from aiohttp import web async def welcome_en(request): name = request.match_info['name'] return web.Response(text=f"Welcome, {name}!") async def welcome_es(request): name = request.match_info['name'] return web.Response(text=f"Bienvenido, {name}!") app = web.Application() app.router.add_get('/en/{name}', welcome_en) app.router.add_get('/es/{name}', welcome_es) web.run_app(app)
Output:
======== Running on http://0.0.0.0:8080 ======== (Press CTRL+C to quit)
This method uses separate handlers for each language.
If you access http://localhost:8080/en/John, it will return “Welcome, John!”, while http://localhost:8080/es/Juan will return “Bienvenido, Juan!”.
Mokhtar is the founder of LikeGeeks.com. He is a seasoned technologist and accomplished author, with expertise in Linux system administration and Python development. Since 2010, Mokhtar has built an impressive career, transitioning from system administration to Python development in 2015. His work spans large corporations to freelance clients around the globe. Alongside his technical work, Mokhtar has authored some insightful books in his field. Known for his innovative solutions, meticulous attention to detail, and high-quality work, Mokhtar continually seeks new challenges within the dynamic field of technology.