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 async def app(scope, receive, send): assert scope['type'] == 'http' request = Request(scope, receive) content = '%s %s' % (request.method, request.url.path) response = Response(content, media_type='text/plain') await response(scope, 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.
Cookies are ignored in case of an invalid cookie. (RFC2109)
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:
async with request.form() as form:
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 async def app(scope, receive, send): assert scope['type'] == 'http' request = Request(scope, receive) body = b'' async for chunk in request.stream(): body += chunk response = Response(body, media_type='text/plain') await response(scope, 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 (
You can configure the number of maximum fields or files with the parameters
async with request.form(max_files=1000, max_fields=1000): ...
These limits are for security reasons, allowing an unlimited number of fields or files could lead to a denial of service attack by consuming a lot of CPU and memory parsing too many empty fields.
When you call
async with request.form() as 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 or
Noneif its not available (e.g.
strwith the content type (MIME type / media type) or
Noneif it's not available (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.
Headersobject. Often this will only be the
Content-Typeheader, but if additional headers were included in the multipart field they will be included here. Note that these headers have no relationship with the headers in
intwith uploaded file's size in bytes. This value is calculated from request's contents, making it better choice to find uploaded file's size than
Noneif not set.
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 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:
async with request.form() as form: filename = form["upload_file"].filename contents = await form["upload_file"].read()
As settled in RFC-7578: 4.2, form-data content part that contains file
assumed to have
filename fields in
name="user"; filename="somefile". Though
filename field is optional according to RFC-7578, it helps
Starlette to differentiate which data should be treated as file. If
filename field was supplied,
object will be created to access underlying file, otherwise form-data part will be parsed and available as a raw
The originating Starlette application can be accessed via
If you want to store additional information on the request you can do so
request.state.time_started = time.time()