Starlette includes a
Request class that gives you a nicer interface onto
the incoming request, rather than accessing the ASGI scope and receive channel directly.
from starlette.requests import Request from starlette.responses import Response class App: def __init__(self, scope): assert scope['type'] == 'http' self.scope = scope async def __call__(self, receive, send): request = Request(self.scope, receive) content = '%s %s' % (request.method, request.url.path) response = Response(content, media_type='text/plain') await response(receive, send)
Requests present a mapping interface, so you can use them in the same
way as a
request['path'] will return the ASGI path.
If you don't need to access the request body you can instantiate a request
without providing an argument to
The request method is accessed as
The request URL is accessed as
The property is a string-like object that exposes all the components that can be parsed out of the URL.
Headers are exposed as an immutable, case-insensitive, multi-dict.
Query parameters are exposed as an immutable multi-dict.
Router path parameters are exposed as a dictionary interface.
The client's remote address is exposed as a named two-tuple
Either item in the tuple may be
The hostname or IP address:
The port number from which the client is connecting:
Cookies are exposed as a regular dictionary interface.
There are a few different interfaces for returning the body of the request:
The request body as bytes:
The request body, parsed as form data or multipart:
The request body, parsed as JSON:
You can also access the request body as a stream, using the
async for syntax:
from starlette.requests import Request from starlette.responses import Response class App: def __init__(self, scope): assert scope['type'] == 'http' self.scope = scope async def __call__(self, receive, send): request = Request(self.scope, receive) body = b'' async for chunk in request.stream(): body += chunk response = Response(body, media_type='text/plain') await response(receive, send)
If you access
.stream() then the byte chunks are provided without storing
the entire body to memory. Any subsequent calls to
will raise an error.
In some cases such as long-polling, or streaming responses you might need to
determine if the client has dropped the connection. You can determine this
disconnected = await request.is_disconnected().
Request files are normally sent as multipart form data (
When you call
await request.form() you receive a
starlette.datastructures.FormData which is an immutable
multidict, containing both file uploads and text input. File upload items are represented as instances of
UploadFile has the following attributes:
strwith the original file name that was uploaded (e.g.
strwith the content type (MIME type / media type) (e.g.
SpooledTemporaryFile(a file-like object). This is the actual Python file that you can pass directly to other functions or libraries that expect a "file-like" object.
UploadFile has the following
async methods. They all call the corresponding file methods underneath (using the internal
async write(data): Writes
bytes) to the file.
async read(size): Reads
int) bytes/characters of the file.
async seek(offset): Goes to the byte position
int) in the file.
await myfile.seek(0)would go to the start of the file.
async close(): Closes the file.
As all these methods are
async methods, you need to "await" them.
For example, you can get the file name and the contents with:
form = await request.form() filename = form["upload_file"].filename contents = await form["upload_file"].read()
If you want to store additional information on the request you can do so
request.state.time_started = time.time()