Skip to content

请求

Starlette 包含一个 Request 类,它为您提供了一个更友好的接口来处理传入的请求,而不是直接访问 ASGI 范围和接收通道。

请求

签名: Request(scope, receive=None)

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)

请求提供了一个映射接口,因此您可以像使用 scope 一样使用它们。

例如: request['path'] 将返回 ASGI 路径。

如果您不需要访问请求主体,则可以在不向 receive 提供参数的情况下实例化请求。

方法

请求方法以 request.method 进行访问。

统一资源定位符(通常也被简称为网址)

请求的 URL 以 request.url 进行访问。

该属性是一个类似字符串的对象,它公开了可以从 URL 中解析出的所有组件。

例如: request.url.pathrequest.url.portrequest.url.scheme

标题

标头被暴露为不可变、不区分大小写的多字典。

例如: request.headers['content-type']

查询参数

查询参数作为不可变的多重字典被暴露出来。

例如: request.query_params['search']

路径参数

路由器路径参数作为字典接口被暴露出来。

例如: request.path_params['username']

客户地址

客户端的远程地址被暴露为一个名为的二元组 request.client (或 None )。

该主机名或 IP 地址: request.client.host

客户端正在连接的端口号: request.client.port

Cookies

Cookie 以常规字典接口的形式呈现。

例如: request.cookies.get('mycookie')

在无效 Cookie 的情况下,Cookie 会被忽略。(RFC2109)

身体

有几种不同的接口用于返回请求的主体:

请求体以字节形式: await request.body()

请求主体,解析为表单数据或多部分: async with request.form() as form:

请求体,解析为 JSON: await request.json()

您还可以使用 async for 语法将请求主体作为流进行访问:

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)

如果您访问 .stream() ,则会提供字节块,而不会将整个主体存储到内存中。任何后续对 .body().form().json() 的调用都将引发错误。

在某些情况下,例如长轮询或流响应,您可能需要确定客户端是否已断开连接。您可以使用 disconnected = await request.is_disconnected() 确定此状态。

请求文件

请求文件通常以多部分表单数据的形式发送( multipart/form-data )。

签名: request.form(max_files=1000, max_fields=1000)

您可以使用参数 max_filesmax_fields 配置最大字段或文件的数量:

async with request.form(max_files=1000, max_fields=1000):
    ...

!!! 信息 出于安全原因设置了这些限制,允许无限数量的字段或文件可能会导致拒绝服务攻击,因为解析过多的空字段会消耗大量的 CPU 和内存。

当您调用 async with request.form() as form 时,您会收到一个 starlette.datastructures.FormData ,它是一个不可变的多字典,包含文件上传和文本输入。文件上传项表示为 starlette.datastructures.UploadFile 的实例。

UploadFile 具有以下属性:

  • filename :具有上传的原始文件名的 str ,或者如果不可用则为 None (例如 myimage.jpg )。
  • content_type :具有内容类型(MIME 类型/媒体类型)的 str ,或者如果不可用(例如 image/jpeg )则为 None
  • file :一个 SpooledTemporaryFile (一个类似文件的对象)。这是实际的 Python 文件,您可以将其直接传递给期望“类似文件”对象的其他函数或库。
  • headers :一个 Headers 对象。通常这只会是 Content-Type 头部,但如果在多部分字段中包含了其他头部,它们也会包含在此处。请注意,这些头部与 Request.headers 中的头部没有关系。
  • size :一个以字节为单位的上传文件大小的 int 。此值根据请求的内容计算得出,因此是确定上传文件大小的更好选择,而非 Content-Length 标头。若未设置,则为 None

UploadFile 具有以下 async 方法。它们都在底层调用相应的文件方法(使用内部 SpooledTemporaryFile )。

  • async write(data) :将 databytes )写入文件。
  • async read(size) :读取文件的 sizeint )字节。
  • async seek(offset) :转到文件中字节位置 offsetint )处。
    • 例如, await myfile.seek(0) 会去到文件的起始位置。
  • async close() :关闭文件。

由于所有这些方法都是 async 方法,您需要“等待”它们。

例如,您可以通过以下方式获取文件名和内容:

async with request.form() as form:
    filename = form["upload_file"].filename
    contents = await form["upload_file"].read()

!!! 信息 如在 RFC - 7578:4.2 中所规定,包含文件的表单数据内容部分假定在 Content-Disposition 头中有 namefilename 字段: Content-Disposition: form-data; name="user"; filename="somefile" 。尽管根据 RFC - 7578, filename 字段是可选的,但它有助于 Starlette 区分哪些数据应被视为文件。如果提供了 filename 字段,将创建 UploadFile 对象以访问底层文件,否则表单数据部分将被解析并作为原始字符串可用。

应用程序

起始的 Starlette 应用程序可通过 request.app 访问。

状态

如果您想在请求上存储其他信息,可以使用 request.state 来实现。

例如:

request.state.time_started = time.time()