Middlewareο
Status: π΄ DA REVISIONARE
What it doesο
Wraps request handling in an ordered chain of cross-cutting stages β error handling, CORS, logging, sessions, auth, and well-known endpoints. Each stage is an object that always exists; you arm it through config.
When to use itο
Whenever you need behaviour that applies across routes rather than inside a single
handler: turning exceptions into clean responses, adding CORS headers, logging
requests, and so on. Most stages are off by default and armed with the
middleware kwarg.
The built-in chainο
The registry ships these middleware, with a priority and a default state. Lower priority number = more outer (runs first on the way in, last on the way out):
Name |
Priority |
Default |
Notes |
|---|---|---|---|
|
100 |
on |
maps exceptions and unmatched paths to status codes |
|
150 |
off |
|
|
200 |
off |
request logging |
|
300 |
off |
CORS headers |
|
400 |
off |
armed automatically by |
|
450 |
off |
armed automatically by |
Only the http scope is processed by the chain.
Two of these arm themselves as a side effect: passing session_store/session_ttl
arms session, and passing auth=... arms auth. You rarely toggle those two
directly.
Setup β arming a stageο
Pass the middleware kwarg. A value of True arms a stage with its defaults; a
dict arms it with options; False disarms it.
from genro_asgi import AsgiServer, RoutedApplication
from genro_routes import route
class App(RoutedApplication):
@route()
def index(self) -> dict:
return {"ok": True}
server = AsgiServer(
primary=App(),
middleware={
"cors": True,
"logging": True,
},
)
server.serve(host="127.0.0.1", port=8000)
CORS optionsο
cors accepts an options dict:
server = AsgiServer(
primary=App(),
middleware={"cors": {
"allow_origins": ["https://example.com"],
"allow_credentials": True,
"max_age": 600,
}},
)
Custom middlewareο
Register your own middleware class in the registry, then arm it like any built-in stage:
from genro_asgi import BaseMiddleware
class StampMiddleware(BaseMiddleware):
...
server = AsgiServer(
primary=App(),
middleware_registry={"stamp": StampMiddleware},
middleware={"stamp": {...}},
)
middleware_registry={"stamp": StampMiddleware}teaches the server the new name.middleware={"stamp": {...}}arms it (and passes its options).
The individual built-in middleware classes are importable from
genro_asgi.middleware (e.g. from genro_asgi.middleware import CORSMiddleware), and BaseMiddleware / MiddlewareMixin from genro_asgi.
How to verify itο
With cors armed, a request carrying an Origin gets CORS headers back:
$ curl -i -H "Origin: https://example.com" http://127.0.0.1:8000/index
HTTP/1.1 200 OK
access-control-allow-origin: https://example.com
With logging armed, requests appear in the serverβs log output.
Gotchasο
errorsis the only stage on by default β it is why an unknown path is a clean404in the hello-world. The rest are off until you arm them.An unknown middleware name in
middleware={...}raisesValueError. If you are arming a custom stage, register it inmiddleware_registryfirst.Ordering is by priority, lower = more outer. A custom stage lands according to its own priority in the chain.
Do not hand-arm
session/authwhen you are already passingsession_store/authβ those kwargs arm the respective middleware for you.