Request and context
This page documents the per-request Context (c) and inbound Request (c.req). Registration, mount, middleware, path/host matching, and what goes into c.route are on Routing. Outbound bytes, Writer, and cookies are on Responses.
Context
Context is the per-request bag the framework constructs before your handler runs. You do not instantiate it yourself.
| Attribute | Purpose |
|---|---|
app | The App instance handling this request. Use for app.url_for, app.create_task, sharing references mounted during bootstrap. |
req | Request: method, path, headers, lazy query/cookies, body readers. |
span | Request telemetry span (started/ended around App.__call__). Use c.span.attr, event, fail for dimensions and errors. |
state | Mutable dict[str, Any]. Middleware can stash values; inner layers and the handler read them. Prefer small, well-known keys. |
route | RouteMatch: canonical pattern string and captured segments. Read values from c.route.params (Route parameters). Empty pattern on 404 / some 405 paths—check before using params. Filled when the app resolves the route, immediately before your handler runs. |
Send the response with w (Writer); read the inbound message from c.req (Request).
Request
Stable view of the request line and headers. The protocol builds it; handlers only read. Reach everything through c.req.
Request fields and accessors
| Member | How you access it | Notes |
|---|---|---|
| HTTP method | c.req.method | str (e.g. "GET"). Fixed when the request starts. |
| Path | c.req.path | Path only; no query string. Fixed at start. |
| Headers | c.req.headers | Headers — case-insensitive keys; get, getlist, items, etc. |
| Protocol | c.req.protocol_version | str, e.g. "1.1". |
| Keep-alive | c.req.keep_alive | bool. |
| Host | c.req.host | Host header without port, lowercased; IPv6 keeps [...]. Lazy on first read. "" if missing. |
| Query string | c.req.query | QueryParams. Lazy. Parsed from the URL’s ?… portion. Use as_dict() / as_lists() when validating the whole query (e.g. Pydantic). |
| Raw query bytes | c.req.query_bytes | Same string as in the URL after ?, without the ? (empty bytes if none). Use when you parse or validate the query yourself. |
| Cookies | c.req.cookies | dict[str, str] merged from Cookie headers. Lazy. |
| Body (buffered) | await c.req.body() | Full body as bytes. Uses the internal reader (size cap, timeouts). Once buffered, later await c.req.body() calls return the same bytes. |
| Body (stream) | async for chunk in c.req.stream(): | Chunked iterator; same limits and errors as body(). If the body was already fully buffered, stream() yields that single cached chunk once. |
On body() and stream():
HttpException(413)if the body exceeds the configured maximum size (default 10 MiB).HttpException(408)if idle between chunks exceeds the body read timeout (slow upload / slowloris guard; default 30 seconds between chunks unless configured otherwise).ClientDisconnectedif the peer closes before the body completes.
Start with either streaming or a full read: if stream() has already started iterating and the body is not yet cached, a second stream() or a conflicting read raises RuntimeError (single in-flight consumer). After the body is fully buffered, body() and stream() can both use the cached bytes without error.
Cookie response headers use stario.cookies with w (Cookies).
class Request(*, method='GET', path='/', query_bytes=b'', protocol_version='1.1', keep_alive=True, headers, body)
Stable snapshot of request-line data plus headers; body I/O goes through an internal BodyReader.
query_bytes is the raw ?-suffix from the URL (no leading ?). host, query, and cookies are computed lazily on first access. Do not reassign query_bytes after construction—it would desynchronize the cached query view.
async Request.body()
Return the entire body as bytes (empty if there is no body reader).
Raises
HttpException: 413 when the body exceeds the configured maximum size. HttpException: 408 when bytes stall longer than the body read timeout (slow upload / slowloris guard). ClientDisconnected: When the peer closes before the body finishes. RuntimeError: If the body was already streamed via stream().
Internally uses BodyReader.read on the protocol-owned reader (size cap, timeouts, backpressure).
async Request.stream()
Stream the body; mutually exclusive with body() for a given request.
Raises
HttpException: 413 / 408 for oversize or stalled uploads (same rules as body()). ClientDisconnected: When the peer closes before the body finishes. RuntimeError: If stream() or body() already consumed this body.
Yields
Consecutive body chunks.
Internally uses BodyReader.stream.
class QueryParams(raw)
View over a parsed query string preserving repeated keys.
QueryParams.as_dict(*, last=True)
One string per key, suitable for Pydantic model_validate and similar.
Repeated keys (?a=1&a=2) keep the first or last value; most UIs send at most one value per key. For every value as a list, use as_lists.
QueryParams.as_lists()
All keys with every repeated value preserved (copy of each list).
Use with schemas whose fields are list[str] (or similar) for ?tag=a&tag=b-style parameters.
QueryParams.get(key, default=None)
First value for key, or default when the key is absent.
QueryParams.getlist(key)
Every value for key (empty list if missing), preserving duplicates from the query string.
QueryParams.items()
All key-value pairs, flattened.
class Headers(raw_header_data=None)
HTTP header map: validated str/bytes API plus r* helpers for already-normalized wire bytes.
Headers.add(name, value)
Append a value, allowing duplicates (e.g. multiple Set-Cookie).
Parameters
- name—Header name (canonical names are validated against known tokens when using
str). - value—Header value; encoded for wire form.
Returns
self for chaining.
Headers.clear()
Remove all headers.
Headers.get(name, default=None)
First header value as str (Latin-1 decoded), or default.
Headers.getlist(name)
Every value for a possibly multi-valued header, as strings.
Headers.items()
Return all header name-value pairs (flattened).
Headers.radd(name, value)
Like add but assumes name is already lowercased header-name bytes (no validation).
Headers.remove(name)
Remove a header.
Headers.rget(name, default=None)
First value as wire bytes (name must be lowercased header bytes).
Headers.rgetlist(name)
All values as wire bytes (for duplicated headers such as Set-Cookie).
Headers.rset(name, value)
Like set for pre-lowercased name bytes (response helpers use this in hot paths).
Headers.set(name, value)
Replace any existing values for this header name.
Parameters
- name—Header name.
- value—New value.
Returns
self for chaining.
Headers.setdefault(name, value)
Set header if not present, return value as string.
Headers.update(other)
class RouteMatch(pattern, params)
Result of routing: a canonical pattern string plus captured path/host segments.
Fields
- pattern(str):Matched route template (useful for logs), including host part when present.
- params(Mapping):Map from
{param}/{rest...}names to decoded segment text.