请求
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.path
, request.url.port
, request.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_files
和 max_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)
:将data
(bytes
)写入文件。async read(size)
:读取文件的size
(int
)字节。async seek(offset)
:转到文件中字节位置offset
(int
)处。- 例如,
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
头中有 name
和 filename
字段: 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()