首页
/ FastAPI Cookie 参数模型(Cookie Parameter Models)实战指南:用 Pydantic 模型批量声明、复用与严格校验 Cookie

FastAPI Cookie 参数模型(Cookie Parameter Models)实战指南:用 Pydantic 模型批量声明、复用与严格校验 Cookie

2026-09-07 10:06:44作者:翟江哲Frasier

在 FastAPI 中,当多个 Cookie 彼此相关时,可以用一个 Pydantic 模型一次性声明它们,从而把"散装"的 Cookie 参数整理成可复用、带校验与元数据的统一结构。本指南以本仓库教程 docs/es/docs/tutorial/cookie-param-models.md(对应英文原文 docs/en/docs/tutorial/cookie-param-models.md)为骨架,结合 docs_src/ 下的官方示例、fastapi/dependencies/utils.pyfastapi/openapi/utils.py 的源码实现,以及 tests/test_tutorial/test_cookie_param_models/ 中的真实测试,完整讲解 Cookie 参数模型的声明方式、校验规则、OpenAPI 文档表现与运行原理。读完你将能写出真正可复制的分组 Cookie 校验代码,并理解"额外 Cookie 被禁止"背后的实现机制。

为什么用 Pydantic 模型声明一组 Cookie

日常开发中,一个请求可能携带多条相关 Cookie,例如 session_id 与各类追踪标识。传统做法是在函数签名中逐个声明:

async def read_items(session_id: str, fatebook_tracker: str | None = None):
    ...

当 Cookie 数量增多,签名会迅速臃肿。把它们收进一个 Pydantic 模型则带来两大收益:

  • 模型可在多处复用:同一个 Cookies 模型可被不同路径操作、不同依赖反复引用,字段口径保持一致;
  • 一次声明校验与元数据:必填、可空、默认值、类型约束、字段说明等,全部集中在一个类定义里,由 FastAPI + Pydantic 统一执行。

需要留意两个前提:

基础用法:把 Cookie 声明成 Pydantic 模型

官方示例 docs_src/cookie_param_models/tutorial001_an_py310.py 给出了完整可运行的最小实现:

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

代码拆解:

  1. 模型字段即 Cookie 名:模型 Cookies 中的 session_idfatebook_trackergoogall_tracker 字段,会逐一对应请求携带的同名 Cookie。
  2. 字段类型决定校验方式session_id: str 是必填字符串;两个 tracker 字段声明为 str | None = None,表示可选、缺省时为 None。这里使用了 Python 3.10+ 的联合类型语法(示例文件名的 py310 后缀即指此意),字段默认值、Field 级校验器(如 min_lengthpattern)等 Pydantic 能力均可照常使用。
  3. Cookie() 标记来源:用 Annotated[Cookies, Cookie()] 把类型标注与"这是从 Cookie 提取"的参数信息绑定在一起,FastAPI 因而知道该从 request.cookies 取数而不是当查询参数或请求体处理。

如果不习惯 Annotated,也可以使用等价的默认值写法(见 docs_src/cookie_param_models/tutorial001_py310.py):

@app.get("/items/")
async def read_items(cookies: Cookies = Cookie()):
    return cookies

两种风格行为完全一致。FastAPI 会从请求携带的 Cookie 中,为模型中的每个字段逐一提取数据,最终交给你的就是填充完毕的 Pydantic 模型实例(此例中模型直接作为 JSON 响应返回)。

底层实现:字段如何被"摊平"再"聚合"

从源码看,模型化的 Cookie 参数并不会整体作为一个值去读取,而是先被摊平为一个个独立字段逐一解析,再重新组装成模型。核心逻辑位于 fastapi/dependencies/utils.py

  • _get_flat_fields_from_params()(约 L157-L166):当某个参数恰好只有一个、且其注解是 BaseModel 子类时,通过 get_cached_model_fields() 取出模型的所有字段作为待处理字段,供 OpenAPI 生成与后续校验使用;
  • request_params_to_args()(约 L780-L850):received_params 在这里就是请求的 Cookie 映射。它先为每个模型字段用 _get_multidict_value() 从 Cookie 中取值(L809-L828),把未在模型中声明但实际收到的 Cookie 也并入待校验字典(L830-L839);当发现是"单个未嵌入的模型字段"时(single_not_embedded_field),会把整份 Cookie 数据一次性交给模型做整体校验(L841-L850),这正是 Pydantic 的 extra 配置能在后文"禁止额外 Cookie"中生效的关键。

运行效果可用如下命令验证(uvicorn 启动后):

# 缺少必填 cookie session_id,返回 422 校验错误
curl -i http://127.0.0.1:8000/items/

# 带上全部 cookie,正常返回模型 JSON
curl -i http://127.0.0.1:8000/items/ \
  -H "Cookie: session_id=123; fatebook_tracker=456; googall_tracker=789"

上述行为的权威佐证来自仓库测试 tests/test_tutorial/test_cookie_param_models/test_tutorial001.py

  • test_cookie_param_model:三个 Cookie 全部设置时返回 200,响应 JSON 与模型字段一一对应;
  • test_cookie_param_model_defaults:只设置 session_id 时,两个可选字段被填充为 None
  • test_cookie_param_model_invalid:完全不带 Cookie 时返回 422,错误定位为 loc: ["cookie", "session_id"],类型 missing,信息 Field required
  • test_cookie_param_model_extra:默认模型配置下多发送一个未声明的 extra Cookie 会被静默忽略(Pydantic v2 默认 extra="ignore"),仍返回 200
  • test_openapi_schema:断言生成的 /openapi.json 中,session_id 等字段各自成为独立参数,且 in: "cookie"required 标志与字段是否必填一致。

注意:示例文件名中的 an(Annotated 风格)与纯 py310 风格两个变体都会被同一组测试参数化覆盖(见测试文件顶部的 pytest.fixtureneeds_py310 标记)。

在 /docs 交互式文档中查看 Cookie

声明完成后,可以打开 /docs 页面确认参数已正确暴露给 API 使用者。下图即官方示例截图,展示本特性的文档界面表现:

FastAPI 交互式文档中基于 Pydantic 模型声明的 Cookie 参数

之所以每个模型字段都能以独立参数出现在文档与 openapi.json 中,是因为 OpenAPI 构建阶段同样使用了"摊平"逻辑:fastapi/openapi/utils.pyget_flat_params(api_route.dependant)(约 L576)会展开模型字段,生成 in: "cookie" 的参数条目。也就是说,Cookie 模型既参与运行时的请求校验,也参与契约(OpenAPI Schema)的自动生成。

一个重要的浏览器限制

在使用 /docs 交互界面测试时请留意:浏览器对 Cookie 有特殊、幕后的管理方式,JavaScript 无法随意读写它们。因此:

  • 你可以在 /docs 的 UI 上看到所有 path operations 的 Cookie 参数文档;
  • 但即便在参数框里填好数据并点击 "Execute",由于文档 UI 是通过 JavaScript 发起请求的,这些 Cookie 并不会真正随请求发送,你会看到类似"未填写任何值"的校验错误。

这不是代码缺陷,而是浏览器安全模型的固有行为。要真实联调 Cookie 场景,应使用带 Cookie 存储的 HTTP 客户端(如 curl-H "Cookie: ..."、浏览器开发者工具,或仓库测试所用的 TestClientcookies.set() 接口)。

进阶:禁止接收额外 Cookie

某些特殊场景下(通常并不常见),你可能希望 API 只接受白名单内的 Cookie,拒绝任何额外项。官方示例 docs_src/cookie_param_models/tutorial002_an_py310.py 展示了通过 Pydantic 模型配置实现这一点:

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"}(纯默认值写法见 docs_src/cookie_param_models/tutorial002_py310.py)。这会把 Pydantic v2 对额外输入的处理策略从默认的 "ignore" 切换为 "forbid",于是任何未在模型中声明的 Cookie 都会触发校验错误响应

例如,若客户端发送了一个值为 good-list-pleasesanta_tracker Cookie,响应将明确告知该 Cookie 不被允许:

{
    "detail": [
        {
            "type": "extra_forbidden",
            "loc": ["cookie", "santa_tracker"],
            "msg": "Extra inputs are not permitted",
            "input": "good-list-please"
        }
    ]
}

从错误结构可以反推实现路径:loc 的第一段是 "cookie"(即 params.Paramin_ 的分类值,见 fastapi/params.pyCookie 类 L387-L388 的定义 in_ = ParamTypes.cookie),第二段是违规的 Cookie 名。这正是前文所述 request_params_to_args() 把未声明 Cookie 并入待校验字典、再交给 _validate_value_with_model_field()整体模型校验的结果——extra_forbidden 错误来自 Pydantic 对多出字段的拒绝。

该行为的权威验证位于 tests/test_tutorial/test_cookie_param_models/test_tutorial002.py

  • test_cookie_param_model / test_cookie_param_model_defaults:白名单内的 Cookie 行为与基础版一致;
  • test_cookie_param_model_extra:在设置了 session_id 后额外发送名为 extra、值为 track-me-here-too 的 Cookie,此时返回 422,错误体为 extra_forbiddenloc["cookie", "extra"]msgExtra inputs are not permittedinput 原样带回非法值;
  • test_openapi_schema:确认开启动态白名单后,OpenAPI 参数列表与基础版一致(模型字段仍是独立 Cookie 参数)。

运行原理与实现细节速览

为了让"模型化 Cookie"与"普通 Cookie 参数"在 FastAPI 内部达成统一,框架做了如下分层处理:

层面 关键位置 作用
参数类型定义 fastapi/params.py Cookie(Param)(L387) 定义 Cookie 参数描述类,in_ = ParamTypes.cookie,继承自 Pydantic 的 FieldInfo
依赖分析与字段摊平 fastapi/dependencies/utils.py _get_flat_fields_from_params(L157) 当单个参数注解为 BaseModel 时,将其展开为多个独立字段,参与请求校验与文档生成
运行时取数 同文件 request_params_to_args(L780) 从 Cookie 映射逐字段取值、合并未声明项,最后整体校验并重建模型
OpenAPI 生成 fastapi/openapi/utils.py get_flat_params(约 L576) 展开模型字段,把每个字段输出为 in: "cookie" 的参数项

值得一提的实现细节:request_params_to_args() 接收的参数源类型是 Mapping | QueryParams | Headers,对 Header 场景还有 convert_underscores 的特殊处理(L799-L803、L811-L828)。Cookie 模型之所以与 Query、Header 模型共享同一套技术,正是因为在源码层面它们都走这一统一的"摊平—取数—重建"管线,只是 field_info.in_.value"cookie" / "query" / "header")不同,相应地读取来源也不同。字段别名方面,取数与错误定位都经由 get_validation_alias() 获得实际使用的键名,因此模型上通过 Pydantic 配置的验证别名同样会被尊重。

如何运行示例与测试

所有示例源码与配套测试都已在仓库内,可以直接验证:

# 运行 Cookie 参数模型教程的全部测试(含 Annotated 与默认值两种风格、extra 行为与 OpenAPI schema)
pytest tests/test_tutorial/test_cookie_param_models/ -q

# 也可启动本地服务手动联调(需先按 pyproject.toml 安装依赖)
python -m uvicorn docs_src.cookie_param_models.tutorial001_an_py310:app --reload

测试中通过 TestClient 的上下文管理器与 cookies.set() 写入模拟 Cookie(见 test_tutorial001.pytest_tutorial002.pyclient fixture),分别覆盖了"全部命中""部分缺省""必填缺失 422""额外 Cookie 忽略/拒绝 422""OpenAPI 参数结构"五类场景,可作为你理解或扩展自定义行为时的参照。此外,文档截图由仓库内的 Playwright 脚本 scripts/playwright/cookie_param_models/image01.py 自动生成,印证了 /docs 页面会以独立参数形式渲染 Cookie 模型字段。

小结

  • 当多个 Cookie 相互关联时,可以在 Pydantic 模型中统一声明,实现跨路径复用与集中式校验/元数据管理;
  • 声明方式是在路径操作参数上使用 Cookie(),并让类型注解指向该模型;Annotated 与默认值两种写法等价;
  • 模型字段名默认即 Cookie 名,必填与否由字段类型与默认值决定,缺少必填 Cookie 会返回 422
  • /docs 中每个字段都会以 in: "cookie" 的独立参数呈现,但由于浏览器限制,文档 UI 的 "Execute" 无法真正发送 Cookie,联调请使用 curl、开发者工具或 TestClient
  • 通过 model_config = {"extra": "forbid"} 可拒绝任何未声明的额外 Cookie,违规响应为 422,错误类型 extra_forbiddenloc 形如 ["cookie", "额外Cookie名"]

以上内容均可在当前仓库中对照验证:教程原文见 docs/es/docs/tutorial/cookie-param-models.mddocs/en/docs/tutorial/cookie-param-models.md,示例代码见 docs_src/cookie_param_models/,实现源码见 fastapi/dependencies/utils.pyfastapi/openapi/utils.py,行为断言见 tests/test_tutorial/test_cookie_param_models/

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388