请求
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()