Routing
BeaverWeb uses decorators to register handlers. Each decorator pairs an HTTP method with a URL path.
HTTP method decoratorsâ
@app.get("/items")
def list_items(req): ...
@app.post("/items")
def create_item(req): ...
@app.put("/items/{id}")
def replace_item(req): ...
@app.patch("/items/{id}")
def update_item(req): ...
@app.delete("/items/{id}")
def delete_item(req): ...
Each decorator registers the handler in the routing table under a (method, path) key. The handler is returned unchanged â there is no wrapping or proxying.
Path parametersâ
Wrap a segment in curly braces to capture it:
@app.get("/users/{id}")
def user_detail(req):
return JSONResponse({"id": req.path_params["id"]})
Multiple parameters are supported:
@app.get("/users/{uid}/posts/{pid}")
def user_post(req):
return JSONResponse(req.path_params)
Captured values are always strings. Coerce inside the handler if a specific type is required:
@app.get("/users/{id}")
def user_detail(req):
user_id = int(req.path_params["id"]) # raises ValueError on non-numeric input
...
Precedence â first registered winsâ
If two routes could match the same URL, the one registered first is selected:
@app.get("/users/me") # register static routes first
def me(req):
return Response("me")
@app.get("/users/{id}") # dynamic routes second
def by_id(req):
return Response(f"id={req.path_params['id']}")
Result:
GET /users/meâmehandlerGET /users/42âby_idhandler
If the order is reversed, /users/me is captured as an id. Register more specific routes before more generic ones.
Trailing slashesâ
BeaverWeb is lenient with trailing and repeated slashes. All of the following resolve to the same route registered as /hello:
/hello/hello//hello//
The root path behaves the same way â / and // both resolve to @app.get("/").
One exception: //hello returns 404 because urllib.parse.urlsplit treats a leading // as a URL authority. This is standard URL parsing behavior.
Method mismatch â 405 with Allowâ
A request to an unsupported method on a known path returns a 405 with an Allow header listing the methods registered for that path:
> POST /users/42
< HTTP/1.1 405 Method Not Allowed
< Allow: GET
When multiple methods are registered:
@app.get("/x")
def g(req): ...
@app.put("/x")
def p(req): ...
# DELETE /x â 405, "Allow: GET, PUT"
Default error responsesâ
| Case | Response |
|---|---|
| Malformed request (bad HTTP line) | 400 Bad Request |
| Path not registered | 404 Not Found |
| Path registered, wrong method | 405 Method Not Allowed with Allow header |
| Handler raises | 500 Internal Server Error (traceback logged) |