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 body_data

application/x-www-form-urlencoded

Individual field kwargs

multipart/form-data

Individual field kwargs; file parts are UploadedFile

Other or missing content type

Bytes in body_raw

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 HTTPException subclass

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.