Add configurable media handlers and Request.data()
- Initially raised as discussion #2012.
Summary
Add application-level media handlers and a generic Request.data() method. This would let third-party packages add support for formats such as MessagePack, CBOR, XML, and YAML without replacing Request or modifying Starlette.
from starlette.applications import Starlette
from starlette_msgpack import MessagePackHandler
app = Starlette(
media_handlers={
"application/msgpack": MessagePackHandler(),
"application/x-msgpack": MessagePackHandler(),
}
)
async def endpoint(request):
data = await request.data()
...Request.body() and Request.stream() would keep returning raw bytes. A handler could choose either API depending on whether it needs to buffer or incrementally consume the request.
Handler protocol
The smallest useful public interface is a callable that receives the request:
from __future__ import annotations
from typing import Any, Protocol
from starlette.requests import Request
class MediaHandler(Protocol):
async def __call__(self, request: Request) -> Any: ...For example, a buffered MessagePack handler could use Request.body():
from __future__ import annotations
from typing import Any
import msgpack
from starlette.requests import Request
class MessagePackHandler:
async def __call__(self, request: Request) -> Any:
return msgpack.unpackb(await request.body(), raw=False)A multipart or sequence handler could consume Request.stream() instead. The protocol does not require Starlette to buffer the body or expose the parser implementation.
Proposed behavior
- Add
media_handlers: Mapping[str, MediaHandler] | None = NonetoStarlette. - Add
await request.data()to select a handler from the normalized requestContent-Typeand cache its result. - Keep
Request.body(),Request.stream(),Request.json(), andRequest.form()backward-compatible. - Provide built-in handlers for JSON, URL-encoded forms, and multipart forms.
- Let application handlers override a built-in media type.
- Raise an unsupported-media-type error when no handler matches.
- Keep decoded streaming APIs out of the initial proposal. Specialized packages can consume
Request.stream()directly.
Exact media types should take precedence. Matching structured suffixes such as application/*+json can be considered as part of the implementation.
Motivation
Starlette currently exposes format-specific methods for JSON and forms. Supporting another media type requires each endpoint to select and invoke a decoder itself, or requires a custom request class. An application-level registry gives dependencies one integration point while keeping the request body APIs small and preserving ASGI streaming and backpressure.
This also keeps implementation choices private. For example, a multipart handler can select between available parser backends while Starlette continues to own limits, temporary files, cleanup, and UploadFile behavior.
Previous discussions
- Discussion #2012 collected the custom request parser proposals. It specifically considered application-level parsers selected by content type and a generic request parsing method.
- Issue #396 included a general
request.parse()method in an earlier API sketch. - Issue #715 requested configurable JSON decoding.
- Issue #78 and issue #304 requested alternative JSON implementations.
- Issue #388, issue #697, and issue #849 requested customizable or streaming multipart handling.
- PR #875 and PR #930 explored custom request classes. The discussion favored generic parsing support on the built-in
Requestinstead.
Source: Kludex/starlette