Added documentation for route path pattern matching.
@@ -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).
|
||||||
|
|||||||
@@ -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 |
|
||||||
|
|||||||
Reference in New Issue
Block a user