FastAPI Cookie 参数模型(Cookie Param Models)实战:用 Pydantic 统一声明、校验与约束 Cookie
本篇技术指南围绕 FastAPI 官方教程中的 Cookie Parameter Models 主题展开,讲解如何把一组业务相关的 Cookie 收拢进一个 Pydantic 模型,用 Cookie() 一次性完成集中声明、类型校验、默认值与 OpenAPI 文档生成,并进一步通过 Pydantic 模型配置来限制并拒绝非预期的"多余 Cookie"。读完本文,你将掌握 Cookie 参数模型的两种写法(Annotated 与默认值风格)、底层解析机制、/docs 交互注意事项,以及 model_config = {"extra": "forbid"} 的严格模式实战用法。
为什么要用 Cookie 参数模型
在 FastAPI 中,单个 Cookie 参数通常像单个查询参数那样逐个声明,写法上你需要在函数签名里为每个 Cookie 单独定义一个参数(可参考 Cookie 参数基础教程)。
但很多场景下 Cookie 是成组出现的,例如:
session_id:会话标识,几乎所有请求都必须携带;fatebook_tracker、googall_tracker:一类分析/追踪型 Cookie,可选且常常伴随默认值;- 更多相互关联的业务 Cookie。
此时更优雅的做法是:把这些 Cookie 声明进一个 Pydantic 模型,再把函数参数的类型标注为这个模型。这样做有两个直接收益:
- 模型可复用:同一个
BaseModel可以被多个路径操作、多个 API 模块重复引用,避免在每处重复罗列参数; - 集中声明校验与元数据:字段的必填/可选、类型、约束、描述等信息只需在模型里写一次,所有使用它的接口统一生效。
该能力自 FastAPI 0.115.0 版本起提供支持。需要注意,同一套"参数模型"技术同样适用于 Query、Cookie 和 Header 三类非 Body 参数(详见 Query 参数模型 与 Header 参数模型),本文以 Cookie 为讲解对象。
快速上手:用 Pydantic 模型声明一组 Cookie
下面是最核心的完整示例,对应仓库中的 cookie_param_models/tutorial001_an_py310.py(Annotated 写法,推荐):
from typing import Annotated
from fastapi import Cookie, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Cookies(BaseModel):
session_id: str
fatebook_tracker: str | None = None
googall_tracker: str | None = None
@app.get("/items/")
async def read_items(cookies: Annotated[Cookies, Cookie()]):
return cookies
代码的关键点拆解如下:
class Cookies(BaseModel):把要接收的 Cookie 作为 Pydantic 模型的字段集中定义。这里session_id: str为必填字段;fatebook_tracker与googall_tracker声明为str | None = None,表示可选,缺省时模型字段值为None。cookies: Annotated[Cookies, Cookie()]:函数参数的类型注解是 Pydantic 模型,再配合Cookie()告诉 FastAPI"这是一组从 Cookie 里提取的参数",而非查询参数或请求体。- 返回模型本身:示例直接
return cookies,FastAPI 会自动把模型序列化回 JSON 返回给客户端。
如果你不想使用 Annotated,也可以采用经典默认值风格,见 cookie_param_models/tutorial001_py310.py:
@app.get("/items/")
async def read_items(cookies: Cookies = Cookie()):
return cookies
请求到达后,FastAPI 会从请求携带的 Cookie 中为模型的每一个字段提取对应数据(按字段名与 Cookie 名一一匹配),校验通过后组装成你定义的 Cookies 模型实例注入函数。
仓库配套测试 test_tutorial001.py 对上述行为给出了直接验证:
- 同时发送
session_id、fatebook_tracker、googall_tracker三个 Cookie,接口返回200,响应体逐一回显三个值; - 只发送
session_id时,返回200,可选字段自动补为None; - 完全不发送任何 Cookie 时返回
422,错误体为:
{
"detail": [
{
"type": "missing",
"loc": ["cookie", "session_id"],
"msg": "Field required",
"input": {}
}
]
}
即:必填的 session_id 缺失会被 Pydantic 校验拦下,错误定位路径 loc 为 ["cookie", "session_id"]。这个细节也说明——参数模型的路由错误与普通 Pydantic 校验错误完全同构,客户端便于统一解析。
底层原理:模型字段如何被拆成独立 Cookie 参数
从源码层面看,FastAPI 在 fastapi/dependencies/utils.py 中会把参数按其"位置类型(in_)"分类收纳。其中将字段挂入依赖的过程在 add_param_to_fields() 函数(约 第 550~563 行)实现:
if field_info_in == params.ParamTypes.path:
dependant.path_params.append(field)
elif field_info_in == params.ParamTypes.query:
dependant.query_params.append(field)
elif field_info_in == params.ParamTypes.header:
dependant.header_params.append(field)
else:
assert field_info_in == params.ParamTypes.cookie, (
f"non-body parameters must be in path, query, header or cookie: {field.name}"
)
dependant.cookie_params.append(field)
这从实现层面印证了:非 Body 的参数只可能归入 path / query / header / cookie 四类之一。Cookie 是 Query、Path 的"姊妹类",共同继承自 Param,其定义位于 fastapi/params.py(class Cookie(Param),约第 387 行);而从 fastapi 包中导入的 Cookie 实际是返回特定参数类的工厂函数(这一点与 Query、Path 一致)。
当参数注解为 Pydantic 模型且配以 Cookie() 时,FastAPI 会把模型中的每个字段分别登记为一个 OpenAPI cookie 参数(每个字段独立出现在文档中,携带各自的必填/可选与类型信息),请求到达后先取整组 Cookie,再做模型级校验与组装。这一点可以从上述测试中的 test_openapi_schema 快照得到印证——生成的 /openapi.json 里 /items/ 的 parameters 包含三个 "in": "cookie" 的参数:
{
"name": "session_id",
"in": "cookie",
"required": true,
"schema": {"type": "string", "title": "Session Id"}
},
{
"name": "fatebook_tracker",
"in": "cookie",
"required": false,
"schema": {
"anyOf": [{"type": "string"}, {"type": "null"}],
"title": "Fatebook Tracker"
}
}
可见:required 标志由字段是否必填自动推导(session_id 无默认值故为必填,两个 tracker 带 None 默认值故为可选、类型含 null)。文档、客户端代码生成与运行时校验由同一份模型定义驱动,不会出现文档与行为不一致的问题。
在 /docs 交互式文档中查看 Cookie 参数
声明好模型后,启动应用并打开 /docs,你可以直观看到这些 Cookie 参数已自动出现在对应路径操作的文档区域。下图展示了示例应用中 GET /items/ 的 Cookie 参数文档界面(图片原始文件):
界面中 session_id 被标记为 required,fatebook_tracker 与 googall_tracker 为非必填,三者位置均标注 (cookie)。
一个浏览器层面的重要限制
请务必注意:浏览器对 Cookie 有特殊且隐蔽的处理机制,通常不允许 JavaScript 直接"触碰"它们。
- 打开
/docs的 API 文档界面,你可以正常看到各路径操作的 Cookie 文档说明; - 但即使你在界面上填好了 Cookie 的值并点击 "Execute",由于该文档 UI 是靠 JavaScript 发请求的,Cookie 并不会被真正发送出去,你将看到类似"未填写任何值"的校验错误。
这意味着在本地用 Swagger UI 直接"试执行"带 Cookie 的接口往往行不通。Cookie 参数模型的接口应当使用真实的浏览器访问(同域 Cookie 自动携带),或使用 TestClient、httpx 等在代码/命令行中显式设置 Cookie 后再请求验证,而不是依赖 /docs 的 Execute 按钮。
严格模式:禁止接收额外的 Cookie
默认情况下,如果客户端多发送了模型中没有声明的 Cookie,FastAPI 会静默忽略这些多余 Cookie 并正常返回 200——仓库测试 test_cookie_param_model_extra 就验证了这一点(额外发送一个 extra Cookie 依然返回成功响应)。
在某些特殊场景(尽管不太常见)下,你可能希望只接受白名单内的 Cookie,拒绝任何多余的 Cookie。此时只需利用 Pydantic 的模型配置把 extra 字段行为设为 forbid,完整代码见 cookie_param_models/tutorial002_an_py310.py:
from typing import Annotated
from fastapi import Cookie, FastAPI
from pydantic import BaseModel
app = FastAPI()
class Cookies(BaseModel):
model_config = {"extra": "forbid"}
session_id: str
fatebook_tracker: str | None = None
googall_tracker: str | None = None
@app.get("/items/")
async def read_items(cookies: Annotated[Cookies, Cookie()]):
return cookies
与前面示例唯一的区别就是增加了一行:
model_config = {"extra": "forbid"}
加上这行后,一旦客户端尝试发送未声明的 Cookie(例如发送一个名为 santa_tracker、值为 good-list-please 的 Cookie),就会收到校验错误响应,而不是被静默忽略:
{
"detail": [
{
"type": "extra_forbidden",
"loc": ["cookie", "santa_tracker"],
"msg": "Extra inputs are not permitted",
"input": "good-list-please"
}
]
}
错误体信息与 Cookie 校验错误保持一致:loc 定位到 ["cookie", "<多余 Cookie 名>"],type 为 extra_forbidden。对应测试实现位于 test_tutorial002.py。需要权衡的是:一旦开启 forbid,后续客户端新增任何追踪、埋点类 Cookie 都会被拒绝,因此仅在你确实需要强约束 Cookie 集合时才开启。
设计要点与使用建议
综合官方文档、配套示例源码(docs_src/cookie_param_models/)与测试(tests/test_tutorial/test_cookie_param_models/),落地 Cookie 参数模型时有几点值得留意:
- 必填字段不要给默认值:像
session_id: str这样无默认值的字段会被推导为必填 Cookie,缺失时返回422,错误定位为["cookie", "session_id"],对客户端排错友好。 - 可选追踪型 Cookie 用
str | None = None:既能保证模型实例化成功,又能把"字段存在但为空"与"字段完全缺失"统一为None,处理逻辑更简单。 - 校验与元数据集中管理:模型字段沿用标准 Pydantic 字段能力,可针对 Cookie 内容做类型转换、长度/格式约束并补充描述信息;一处定义,多接口复用,OpenAPI 文档与运行时校验自动保持一致。
- Cookie 模型只适用于合适的位置:从底层源码可看到非 Body 参数必须落入 path / query / header / cookie 四类,Cookie 参数模型必须配合
Cookie()使用,否则参数会被当作查询参数处理。 - 严格模式按需开启:
extra="forbid"会拒绝白名单外的任意 Cookie,适合安全敏感或内部专用的接口;普通对外接口建议保持默认的"忽略多余项"行为,避免过度耦合客户端行为。
小结
本文完整覆盖了 FastAPI Cookie 参数模型从声明到落地的全链路:用 Pydantic BaseModel 集中声明一组 Cookie 并通过 Cookie() 注入路径操作(自 FastAPI 0.115.0 起支持)、两种函数签名写法、/docs 中文档展示及其浏览器 Cookie 限制、底层参数分组机制,以及用 model_config = {"extra": "forbid"} 拒绝多余 Cookie 的严格模式。该技术与 Query 参数模型、Header 参数模型同源同法,掌握一种即可触类旁通;如果你还希望对模型的单个字段做更强约束,可进一步结合 Pydantic Field 与 Field Validator 扩展,相关示例与测试都可在上述 docs_src 与 tests 目录中对照研读。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
