Requests and errors
Status: Draft; implementation checked against the development source on 2026-09-08.
RoutedApplication creates a Request and awaits init() before resolving and
calling a handler. It drains the entire ASGI body and decodes it by content type.
It uses genro-tytx for serialization, not for reading the ASGI protocol.
Body arguments
Content type |
Handler arguments |
|---|---|
JSON, XML, msgpack (including TYTX media types) |
Hydrated value in |
|
Individual field kwargs |
|
Individual field kwargs; file parts are |
Other or missing content type |
Bytes in |
Empty body |
No body argument |
Query parameters form the initial kwargs. Repeated query keys become lists;
form fields override query fields with the same name. Repeated multipart names
also become lists. A file has name (form field name), filename (client-supplied),
content_type and data (complete bytes). Uploaded files are not spooled to disk.
Save this as bodies.py, then run python bodies.py:
from genro_asgi import AsgiServer, RoutedApplication
from genro_routes import route
class Bodies(RoutedApplication):
mount = ""
@route()
def document(self, body_data):
return {"received": body_data}
@route()
def upload(self, title, document):
return {"title": title, "filename": document.filename,
"bytes": len(document.data)}
@route()
def raw(self, body_raw):
return {"bytes": len(body_raw)}
if __name__ == "__main__":
AsgiServer(applications=[Bodies()]).serve(host="127.0.0.1", port=8000)
$ curl -H 'Content-Type: application/json' -d '{"name":"Ada"}' http://127.0.0.1:8000/document
{"received":{"name":"Ada"}}
$ curl -F title=Example -F document=@bodies.py http://127.0.0.1:8000/upload
{"title":"Example","filename":"bodies.py","bytes":...}
$ curl -H 'Content-Type: application/octet-stream' --data-binary abc http://127.0.0.1:8000/raw
{"bytes":3}
Stop the process with Ctrl-C. There is no configurable total body-size limit in
Request.read_body(): memory grows with the body, including multipart uploads.
Use an ingress limit or an application that controls receive directly when
unbounded uploads are unacceptable. Direct HTTP response streaming does not
change this request buffering; see Streaming.
Validation and status codes
AsgiServer automatically arms pydantic and openapi on its routed
applications. Neither can be disabled; explicit entries configure their
options. A composition without PluginMixin does not supply this pair.
Pydantic coerces and validates annotated parameters. A JSON dictionary is
spread over declared scalar parameters
unless the handler declares body_data or accepts **kwargs. Extra JSON keys
are dropped in that spreading case. Forms and query kwargs still bind normally.
Condition |
Status |
|---|---|
Unknown route |
404 |
Missing required argument or unexpected kwarg |
400 |
Signature fits, but configured pydantic validation rejects values |
422 |
Exception raised inside the handler body |
500, unless it is an HTTP exception |
Handler raises an |
That exception’s status |
Malformed payload decoding is separate from signature validation: do not assume that every parse error becomes 400 or 422. See the error middleware and request API for the current mappings.
A handler that declares an unannotated _request parameter can receive the
live request when parameter metadata is available. AsgiServer supplies that
metadata through its automatically armed pydantic plugin.
It exposes the session and avatar(), and its response can be given cookies or
headers before the handler returns its result. This is explicit parameter
injection, not a thread-local current request.