Added documentation for route path pattern matching.

2026-02-08 13:44:55 -05:00
parent 1b9bf1d837
commit 25e541790a
2 changed files with 72 additions and 0 deletions
+1
@@ -119,6 +119,7 @@ async def stats_page(ctx: RouteContext) -> dict:
| Field | Type | Description | | Field | Type | Description |
|-------|------|-------------| |-------|------|-------------|
| `request` | `aiohttp.web.Request` | The aiohttp request object. Access query params, body, headers, path info, etc. | | `request` | `aiohttp.web.Request` | The aiohttp request object. Access query params, body, headers, path info, etc. |
| `match_info` | `dict[str, str]` | Captured path parameters from `{name}` patterns. Empty dict for plain routes. |
| `module` | `ModuleContext` | The full module context with all services. | | `module` | `ModuleContext` | The full module context with all services. |
All `ModuleContext` services are available directly on `ctx` (see [Proxied Properties](#proxied-properties) below). All `ModuleContext` services are available directly on `ctx` (see [Proxied Properties](#proxied-properties) below).
+71
@@ -43,6 +43,60 @@ Routes are automatically prefixed under `/owlbot/<module_name>/`. So if the modu
This avoids conflicts between modules and with the webhook endpoint. Only the relative path is needed in the decorator. This avoids conflicts between modules and with the webhook endpoint. Only the relative path is needed in the decorator.
## Path Parameters
Route paths support `{name}` patterns for capturing dynamic segments, using the same syntax as aiohttp. When a request arrives, the URL path is matched against registered route patterns and the captured segments populate `ctx.match_info`.
Given the route `@on_route("/users/{id}/posts/{slug}")` and a request to `/owlbot/my_module/users/42/posts/hello-world`:
| Property | Value |
|----------|-------|
| `ctx.match_info["id"]` | `"42"` |
| `ctx.match_info["slug"]` | `"hello-world"` |
For plain routes without `{...}` patterns, `ctx.match_info` is an empty dict `{}`.
### Single Segment
`{name}` matches a single path segment (no `/`):
```python
@on_route("/users/{id}")
async def get_user(ctx: RouteContext) -> dict:
user_id = ctx.match_info["id"] # e.g., "123"
return {"user_id": user_id}
```
Registered as `/owlbot/my_module/users/{id}`, this matches `/owlbot/my_module/users/123` but not `/owlbot/my_module/users/123/posts`.
### Custom Regex
`{name:regex}` matches with a custom regex pattern:
```python
@on_route("/users/{id:\\d+}")
async def get_user(ctx: RouteContext) -> dict:
user_id = ctx.match_info["id"] # only digits
return {"user_id": int(user_id)}
```
### Catch-All
`{name:.*}` matches the remaining path, including `/` characters. This is useful for prefix-style routing:
```python
@on_route("/api/{path:.*}", methods=["GET", "POST"])
async def api_catchall(ctx: RouteContext) -> dict:
remaining = ctx.match_info["path"] # e.g., "users/123/posts"
return {"path": remaining}
```
> **Note:** The catch-all requires at least one character after the prefix. `/api/{path:.*}` matches `/api/x` but not `/api`.
### Match Order
Routes are matched in registration order (first match wins). Routes defined with `@on_route` at import time are registered before dynamically registered routes, so they naturally get priority.
## Response Types ## Response Types
Route handlers support three return types: Route handlers support three return types:
@@ -128,6 +182,9 @@ async def list_page(ctx: RouteContext) -> web.Response:
`ctx.request` is a standard `aiohttp.web.Request`. Some common patterns: `ctx.request` is a standard `aiohttp.web.Request`. Some common patterns:
```python ```python
# Path parameters (from {name} patterns):
user_id = ctx.match_info["id"] # e.g., "123"
# Query parameters: # Query parameters:
format = ctx.request.query.get("format", "json") format = ctx.request.query.get("format", "json")
@@ -165,6 +222,20 @@ async def setup(ctx: ModuleContext) -> None:
) )
``` ```
Dynamic registration also supports path patterns:
```python
async def user_handler(ctx: RouteContext) -> dict:
return {"user_id": ctx.match_info["id"]}
@on_setup
async def setup(ctx: ModuleContext) -> None:
ctx.routes.register(
path="/users/{id}",
handler=user_handler,
)
```
### Available Methods ### Available Methods
| Method | Description | | Method | Description |