Litestar 连接层(connection)API 全解析:ASGIConnection、Request 与 WebSocket 的底层原理与实战
2026/9/16 19:07:13 网站建设 项目流程

Litestar 连接层(connection)API 全解析:ASGIConnection、Request 与 WebSocket 的底层原理与实战

【免费下载链接】litestarLight, flexible and extensible ASGI framework | Built to scale项目地址: https://gitcode.com/GitHub_Trending/li/litestar

Litestar 的litestar.connection模块是所有 HTTP 请求与 WebSocket 连接处理的核心基座,它把 ASGI Scope/Receive/Send 协议封装成开发者友好的ASGIConnectionRequestWebSocket三个类。本文以 docs/reference/connection.rst 为骨架,结合 litestar/connection/ 包源码与 tests/unit/test_connection/ 测试,系统讲解这三个连接类的每个属性与方法,帮助你理解 Litestar 的路由处理、依赖注入、鉴权、Session 与 WebSocket 通信是如何在连接层之上构建的。

模块概览:三个类各司其职

litestar.connection包的对外导出非常精简,全部公开符号只有三个类(见 litestar/connection/init.py):

from litestar.connection.base import ASGIConnection from litestar.connection.request import Request from litestar.connection.websocket import WebSocket __all__ = ("ASGIConnection", "Request", "WebSocket")
基类对应 Scope 类型职责
ASGIConnectionGeneric[HandlerT, UserT, AuthT, StateT]通用 ASGI Scope连接级公共能力:URL、头、参数、Cookie、State、Session、auth/user 等
RequestASGIConnection["HTTPRouteHandler", ...]HTTPScopeHTTP 专属能力:方法、正文、表单、JSON/MessagePack、流式读取、Server Push
WebSocketASGIConnection["WebsocketRouteHandler", ...]WebSocketScopeWebSocket 专属能力:握手、收发文本/二进制/JSON/MessagePack

三者共享同一套scopereceivesend三件套构造参数,默认值分别为empty_receiveempty_send——这两个占位函数在被调用时会直接抛出RuntimeError,用于阻止在不需要收发的场景(例如仅读取 Scope 数据)下误用网络通道(litestar/connection/base.py)。

值得注意的继承细节:Request的泛型参数把HandlerT绑定为HTTPRouteHandlerWebSocket则绑定为WebsocketRouteHandler,因此connection.route_handler属性的静态类型会自动收窄为对应的处理器类型,方便直接访问路由处理器上的配置(如route_handler.type_decodersroute_handler.request_max_body_size)。

ASGIConnection:所有连接的公共基座

ASGIConnection使用__slots__声明了仅有的几个实例字段,内存占用可控;其余能力全部通过属性(property)惰性计算并借助ScopeState进行连接级缓存(litestar/connection/base.py)。

三个原始协议对象:scope / receive / send

  • scope:ASGI 连接作用域字典,其中litestar_approute_handlerpath_paramsstate等键由 Litestar 框架在路由分发阶段写入;
  • receive:异步接收函数,用于读取 ASGI 事件(如http.requestwebsocket.receive);
  • send:异步发送函数,用于写出 ASGI 事件(如http.response.startwebsocket.send)。

常用只读属性一览

属性类型说明底层来源
appLitestar当前应用实例scope["litestar_app"]
route_handlerHandlerT命中当前连接的路由处理器scope["route_handler"]
stateState应用级 State(基于scope["state"]构造)datastructures/state.py
urlURL完整请求 URLURL.from_scope(scope),结果缓存在ScopeState.url
base_urlURL仅 scheme + netloc + root_path 的基础 URL由 Scope 改造后重新解析(见下文)
headersHeaders请求头,大小写不敏感的多值字典Headers.from_scope(scope)
query_paramsMultiDict查询参数,同键多值自动聚合成列表parse_query_string
path_paramsdict路径参数scope["path_params"]
cookiesdict[str, str]解析后的 Cookie 字典cookie请求头解析
clientAddress \| None客户端地址(host, port),Address是命名元组scope["client"]
authAuthT当前认证主体,未安装 AuthMiddleware 则抛异常scope["auth"]
userUserT当前用户对象,同上scope["user"]
sessiondict当前 Session 数据scope["session"]

auth / user / session 的防护性设计:三个属性在 scope 中找不到对应键时都会抛出ImproperlyConfiguredException,并给出明确的提示文案——例如auth会提示"install an AuthMiddleware to set it",session会提示"install a SessionMiddleware to set it"(litestar/connection/base.py)。这意味着这些属性不是"默认返回 None",而是强制要求通过对应的中间件显式注入,从源头避免静默的空值传递。

base_url 的构造技巧:与url直接解析原始 scope 不同,base_url会先构造一个副本 scope——把path置为"/"、清空query_string、并用app_root_pathroot_path覆盖root_path——再调用URL.from_scope,从而得到 "scheme://host[:port]/root_path" 这样的纯基础地址,方便后续拼绝对 URL(litestar/connection/base.py)。

惰性缓存机制:URL、base_url、query_params、cookies 等解析结果都会写入连接对应的ScopeStateurlbase_urlparsed_querycookies等槽位),首次访问后后续访问直接命中缓存。这一点在 tests/unit/test_connection/test_base.py 中有直接验证:访问connection.url前后,ScopeState.urlEmpty变为非空。

会话管理:set_session / clear_session / get_session_id

  • set_session(value):把字典(或 pydantic 等数据容器、或Empty)写入scope["session"];若启用了SessionMiddleware,后续响应阶段会据此生成 Cookie 头;
  • clear_session():把scope["session"]置为Empty并清空ScopeState.session_id,使中间件感知到"会话已清空";
  • get_session_id():返回ScopeState.session_id,未设置时返回None

反向 URL 生成:url_for

url_for(name, **path_parameters)是连接层最重要的工具方法之一:它通过app.route_reverse(name, **path_parameters)将路由处理器或路由名解析为路径,再用make_absolute_url(path, self.base_url)拼成绝对 URL(litestar/connection/base.py)。当路由名不存在、路径参数缺失或类型错误时抛出NoRouteMatchFoundException。注意make_absolute_url会先对base_url的 path 做rstrip("/"),避免出现双斜杠。

Request:HTTP 请求的完整封装

RequestASGIConnection基础上增加了 HTTP 协议相关的全部能力(litestar/connection/request.py),并新增两个实例标志位:is_connected(正文流是否仍可读取)与supports_push_promise(ASGI 服务器是否支持http.response.push扩展,检测逻辑见 enums.py 中的ASGIExtension.SERVER_PUSH)。

HTTP 元数据属性

属性类型说明
methodMethodHTTP 方法,直接取自scope["method"]
content_typetuple[str, dict[str, str]]解析后的Content-Type,返回主值 + 选项字典(如 multipart 的boundary
acceptAccept解析后的Accept头,支持媒体类型优先级与通配符(默认*/*
content_lengthint \| None解析content-length头;值非法时抛出ClientException

content_typeaccept同样走ScopeState缓存(content_typeaccept槽位),避免重复解析。

正文读取:body / stream / json / msgpack

  • await request.body():一次性聚合全部正文为bytes
  • request.stream():异步生成器,逐块产出字节流。其内部实现了请求体大小上限的双重校验
    1. content-length头声明的大小超过route_handler.request_max_body_size(未配置则为math.inf),在读取任何字节前直接抛RequestEntityTooLarge(413);
    2. 实际累计读取字节数超过上限时也会抛 413——注释特别说明这一层校验是有意保留的,因为请求解压等中间件可能导致流式正文合法地超过 wire 格式的 content-length;
    • 若收到http.disconnect事件则抛InternalServerException("client disconnected prematurely");流被消费过后再次调用会抛InternalServerException("stream consumed")
  • await request.json():读取正文并反序列化为任意 Python 对象,空正文按b"null"处理,支持route_handler.type_decoders注册的自定义解码器;
  • await request.msgpack():MessagePack 反序列化,空正文按b"\xc0"(nil)处理。

json()msgpack()的结果同样会被缓存(ScopeState.jsonScopeState.msgpack),多次调用只解析一次。

表单解析:await request.form()

form()根据Content-Type分派三种路径(litestar/connection/request.py):

  1. multipart/form-data:从 options 中取出boundary(缺失则抛ValidationException),调用parse_multipart_form流式解析,部件数量上限受app.multipart_form_part_limit约束;
  2. application/x-www-form-urlencoded:读取正文后调用parse_url_encoded_form_data解析;
  3. 其他类型:返回空结果。

最终统一包装为FormMultiDict(不可变多值字典,见 datastructures/multi_dicts.py)返回,同样支持连接级缓存。

HTTP/2 Server Push:send_push_promise

send_push_promise(path, raise_if_unavailable=False)用于在支持http.response.push扩展的服务器上主动推送资源。其行为分为三档:

  • 服务器支持:从当前请求头中挑选SERVER_PUSH_HEADERSacceptaccept-encodingaccept-languagecache-controluser-agent,定义于 litestar/connection/request.py)构造推送给定的请求头,然后发送http.response.push事件;
  • 服务器不支持且raise_if_unavailable=True:抛LitestarException
  • 服务器不支持且未设置:发出LitestarWarning,并提示可通过request.supports_push_promise动态检测(未来版本将默认改为抛异常)。

WebSocket:全双工通信的完整封装

WebSocket类在构造时就用receive_wrapper/send_wrapper包装了底层收发函数,用connection_state字段(取值"init" | "connect" | "receive" | "disconnect")跟踪连接生命周期,并依据状态自动拦截异常行为(litestar/connection/websocket.py):

  • receive_wrapper:根据收到的 ASGI 事件类型更新状态——websocket.connect"connect"websocket.receive"receive"、其余(含 disconnect)→"disconnect";若状态已是"disconnect"仍尝试接收,直接抛WebSocketDisconnect
  • send_wrapper:发送前校验状态,已断开则抛WebSocketDisconnect

握手与关闭

  • await ws.accept(subprotocols=None, headers=None):接受连接,可指定子协议与响应头(支持Headersdict(bytes, bytes)列表三种形式,统一转换为list[tuple[bytes, bytes]]写入websocket.accept事件);状态为"init"时会先自动消费 connect 事件;
  • await ws.close(code=WS_1000_NORMAL_CLOSURE, reason=None):发送websocket.close事件,默认状态码为 1000(正常关闭,定义于 status_codes.py)。

数据接收 API

方法说明
receive_data(mode="text" \| "binary")接收单条数据;收到 disconnect 事件则抛WebSocketDisconnect(detail="disconnect event", code=...);状态为init时先自动 accept
receive_text()/receive_bytes()便捷封装,分别对应 text / binary 模式
receive_json(mode="text")接收并反序列化 JSON,支持route_handler.type_decoders
receive_msgpack()接收并反序列化 MessagePack(始终以 binary 模式接收,因为 MessagePack 是二进制格式)
iter_data(mode="text")异步生成器,持续 yield 数据直至WebSocketDisconnect被吞掉并正常结束
iter_json(mode)/iter_msgpack()持续接收并逐条反序列化

数据发送 API

方法说明
send_data(data, mode="text", encoding="utf-8")底层发送;text 模式把 bytes 按 encoding 解码为 str,binary 模式把 str 按 encoding 编码为 bytes;状态为init时先自动 accept
send_text(data, encoding="utf-8")以 text 键发送
send_bytes(data, encoding="utf-8")以 bytes 键发送
send_json(data, mode="text", encoding="utf-8", serializer=default_serializer)序列化为 JSON 后发送,可自定义serializer
send_msgpack(data, encoding="utf-8", serializer=default_serializer)序列化为 MessagePack 后发送(始终以 binary 模式发送

JSON/MessagePack 的编解码统一经由 serialization/msgspec_hooks.py 暴露的decode_json/encode_json/decode_msgpack/encode_msgpack完成,默认序列化器为default_serializer

连接层在依赖注入中的应用

RequestWebSocket都是可以直接声明为依赖参数的类型:在路由处理器或依赖函数中把参数类型标注为Request/WebSocket(或其子类),Litestar 的参数解析器(litestar/_kwargs/extractors.py)就会注入对应连接实例。此时可以放心使用request.apprequest.staterequest.authrequest.userrequest.session等能力,与 docs/usage/dependency-injection.rst 描述的整体机制协同工作。

典型实战组合:鉴权 + Session + URL 生成

综合上述 API,一个典型的受保护接口可以这样组织:

from litestar import get from litestar.connection import Request @get("/account", name="account") async def account(request: Request) -> dict: # 依赖 AuthMiddleware 注入的 user / auth user = request.user # 读写 Session(依赖 SessionMiddleware) request.set_session({"last_visited": "/account"}) # 生成指向其他路由的绝对 URL profile_url = request.url_for("account") return {"user": str(user), "url": profile_url}
  • 若未安装AuthMiddleware而访问request.user,会立即抛出ImproperlyConfiguredException,提示正确装配方式;
  • request.url_for("account")基于base_url拼接,挂载在子应用前缀下也能得到正确的绝对地址。

与测试的对应关系

连接层的核心行为都有单元测试覆盖(tests/unit/test_connection/):

  • test_base.py:验证ASGIConnection各属性与ScopeState缓存联动、auth/user/session的注入读取;
  • test_connection_caching.py:验证 URL、query、cookies 等解析结果的连接级缓存行为;
  • test_request.py:覆盖正文、表单、JSON/MessagePack、content-length 异常等 HTTP 能力;
  • test_websocket.py:覆盖握手、收发各模式、断连异常与迭代器行为。

在编写自定义中间件、自定义 Request 子类(参考 docs/examples/request_data/custom_request.py)或直接处理 WebSocket 时,这三份测试是理解预期行为的最佳范本。

小结

litestar.connection模块通过ASGIConnectionRequest/WebSocket的两级继承,把 ASGI 协议细节统一收纳进一套带缓存、带状态机、带异常防护的连接 API:

  • ASGIConnection提供跨协议公共能力(URL/头/参数/Cookie/State/Session/auth/user/url_for);
  • Request补全 HTTP 正文、表单、JSON/MessagePack 与 Server Push;
  • WebSocket用状态机管理全双工收发的握手、编解码与断连语义。

理解这一层抽象,也就理解了 Litestar 的依赖注入、鉴权、Session 中间件乃至 WebSocket 子应用为什么能如此简洁地共享同一套上下文,是深入框架内部的理想切入点。

【免费下载链接】litestarLight, flexible and extensible ASGI framework | Built to scale项目地址: https://gitcode.com/GitHub_Trending/li/litestar

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询