starlette/docs/requests.md

5.0 KiB

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.

Request

Signature: Request(scope, receive=None)

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 scope.

For instance: 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 receive.

Method

The request method is accessed as request.method.

URL

The request URL is accessed as request.url.

The property is a string-like object that exposes all the components that can be parsed out of the URL.

For example: request.url.path, request.url.port, request.url.scheme.

Headers

Headers are exposed as an immutable, case-insensitive, multi-dict.

For example: request.headers['content-type']

Query Parameters

Query parameters are exposed as an immutable multi-dict.

For example: request.query_params['search']

Path Parameters

Router path parameters are exposed as a dictionary interface.

For example: request.path_params['username']

Client Address

The client's remote address is exposed as a named two-tuple request.client. Either item in the tuple may be None.

The hostname or IP address: request.client.host

The port number from which the client is connecting: request.client.port

Cookies

Cookies are exposed as a regular dictionary interface.

For example: request.cookies.get('mycookie')

Body

There are a few different interfaces for returning the body of the request:

The request body as bytes: await request.body()

The request body, parsed as form data or multipart: await request.form()

The request body, parsed as JSON: await request.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 .body(), .form(), or .json() 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 state with disconnected = await request.is_disconnected().

Request Files

Request files are normally sent as multipart form data (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 starlette.datastructures.UploadFile.

UploadFile has the following attributes:

  • filename: A str with the original file name that was uploaded (e.g. myimage.jpg).
  • content_type: A str with the content type (MIME type / media type) (e.g. image/jpeg).
  • file: A 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 SpooledTemporaryFile).

  • async write(data): Writes data (str or bytes) to the file.
  • async read(size): Reads size (int) bytes/characters of the file.
  • async seek(offset): Goes to the byte position offset (int) in the file.
    • E.g., 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()

Other state

If you want to store additional information on the request you can do so using request.state.

For example:

request.state.time_started = time.time()