litestar请求

2 阅读4分钟

litestar请求

本文内容基于《官方文档-请求》的部分进行制作,对其内容大幅进行缩减,使其能适合新手入门,如何想充分学习litestar可以直接查看官方文档

1. 正常请求

from litestar import Litestar, post

@post(path="/")
async def index(data: dict[str, str]) -> dict[str, str]:
    return data

app = Litestar(route_handlers=[index])

正常情况下data为post上来的json主体

也支持:MsgspecPydanticAttrs

from dataclasses import dataclass
from litestar import Litestar, post

@dataclass
class User:
    id: int
    name: str

@post(path="/")
async def index(data: User) -> User:
    return data


app = Litestar(route_handlers=[index])

2. 自定义文档

from dataclasses import dataclass

from typing import Annotated

from litestar import Litestar, post
from litestar.params import Body


@dataclass
class User:
    id: int
    name: str


@post(path="/")
async def create_user(
    data: Annotated[User, Body(title="Create User", description="Create a new user.")],
) -> User:
    return data


app = Litestar(route_handlers=[create_user])

3. Content-type

默认情况下,Litestar 将尝试将请求正文解析为 JSON。可以通过body设置RequestEncodingType修改

3.1. x-www-form-urlencoded格式数据

要访问作为 url 编码的表单数据 (即application/x-www-form-urlencoded Content-Type标头)发送的数据,请使用 Body 并指定 RequestEncodingType.URL_ENCODED作为media_type

from dataclasses import dataclass

from typing import Annotated

from litestar import Litestar, post
from litestar.enums import RequestEncodingType
from litestar.params import Body


@dataclass
class User:
    id: int
    name: str


@post(path="/")
async def create_user(
    data: Annotated[User, Body(media_type=RequestEncodingType.URL_ENCODED)],
) -> User:
    return data


app = Litestar(route_handlers=[create_user])

URL 编码数据本质上不如 JSON 数据通用 - 例如,它无法处理复杂的字典和深度嵌套的数据。它只应用于简单的数据结构。

3.2. multipart格式数据

Content-Type标头为multipart/form-data

from dataclasses import dataclass

from typing import Annotated

from litestar import Litestar, post
from litestar.datastructures import UploadFile
from litestar.enums import RequestEncodingType
from litestar.params import Body


@dataclass
class User:
    id: int
    name: str
    form_input_name: UploadFile


@post(path="/")
async def create_user(
    data: Annotated[User, Body(media_type=RequestEncodingType.MULTI_PART)],
) -> dict[str, str]:
    content = await data.form_input_name.read()
    filename = data.form_input_name.filename
    return {"id": data.id, "name": data.name, "filename": filename, "size": len(content)}


app = Litestar(route_handlers=[create_user])
3.2.1. 文件作为字典
from typing import Annotated

from litestar import Litestar, post
from litestar.datastructures import UploadFile
from litestar.enums import RequestEncodingType
from litestar.params import Body


@post(path="/")
async def handle_file_upload(
    data: Annotated[dict[str, UploadFile], Body(media_type=RequestEncodingType.MULTI_PART)],
) -> dict[str, str]:
    file_contents = {}
    for name, file in data.items():
        content = await file.read()
        file_contents[file.filename] = len(content)

    return file_contents


app = Litestar(route_handlers=[handle_file_upload])
3.2.2. 文件列表
from typing import Any

from typing import Annotated

from litestar import Litestar, post
from litestar.datastructures import UploadFile
from litestar.enums import RequestEncodingType
from litestar.params import Body


@post(path="/")
async def handle_file_upload(
    data: Annotated[list[UploadFile], Body(media_type=RequestEncodingType.MULTI_PART)],
) -> dict[str, tuple[str, str, Any]]:
    result = {}

    for file in data:
        content = await file.read()
        result[file.filename] = (len(content), file.content_type, file.headers)

    return result


app = Litestar(route_handlers=[handle_file_upload])

3.3. MessagePack格式数据

from typing import Any

from typing import Annotated

from litestar import Litestar, post
from litestar.enums import RequestEncodingType
from litestar.params import Body


@post(path="/", sync_to_thread=False)
def msgpack_handler(
    data: Annotated[dict[str, Any], Body(media_type=RequestEncodingType.MESSAGEPACK)],
) -> dict[str, Any]:
    # This will try to parse the request body as `MessagePack` regardless of the
    # `Content-Type`
    return data


app = Litestar(route_handlers=[msgpack_handler])

4. 自定义请求

2.7.0 新版功能

Litestar 支持自定义request_class实例,可用于进一步配置默认 Request。下面的示例说明了如何为整个应用程序实现自定义请求类。

from litestar import Litestar, Request, get
from litestar.connection.base import empty_receive, empty_send
from litestar.enums import HttpMethod
from litestar.types import Receive, Scope, Send

KITTEN_NAMES_MAP = {
    HttpMethod.GET: "Whiskers",
}


class CustomRequest(Request):
    """Enrich request with the kitten name."""

    __slots__ = ("kitten_name",)

    def __init__(self, scope: Scope, receive: Receive = empty_receive, send: Send = empty_send) -> None:
        """Initialize CustomRequest class."""
        super().__init__(scope=scope, receive=receive, send=send)
        self.kitten_name = KITTEN_NAMES_MAP.get(scope["method"], "Mittens")


@get(path="/kitten-name", sync_to_thread=False)
def get_kitten_name(request: CustomRequest) -> str:
    """Get kitten name based on the HTTP method."""
    return request.kitten_name


app = Litestar(
    route_handlers=[get_kitten_name],
    request_class=CustomRequest,
    debug=True,
)

5. 限制

可以通过 request_max_body_size 参数,默认为 10MB,此限制适用于使用请求的所有方法 body,包括通过路由处理程序中的 body 参数请求它,以及通过手动构建的Request使用它实例,例如在中间件中。

要为特定处理程序/路由器/控制器禁用此限制,可以将其设置为None

强烈建议不要设置 request_max_body_size=None,因为它会通过向受影响的终端节点发送任意大的请求正文来使应用程序遭受拒绝服务 (DoS) 攻击。因为 Litestar 必须读取整个正文才能执行某些作,例如解析 JSON,所以它将填满所有可用的内存/交换,直到应用程序/服务器崩溃,如果没有施加外部限制。 通常,仅在应用程序在反向代理(如 NGINX)后面运行的环境中,才建议这样做,因为此时已经设置了大小限制。

由于 request_max_body_size 是按请求处理的,因此不会影响 middlewares 或 ASGI 处理程序,当它们尝试通过原始 ASGI 活动。为了避免这种情况,中间件和 ASGI 处理程序应该构造一个request实例并使用常规的 stream() / body()或内容适当的方法以安全的方式使用请求正文。

提示:对于定义 Content-Length 标头的请求,如果标头值超过 request_max_body_sizeLitestar将不会尝试读取请求正文。