FastAPI Cookie 参数(Cookie Parameters)声明指南:从基础用法到源码级解析
读取 HTTP Cookie 并向路径操作函数注入其值,是开发登录态、会话与广告追踪等场景的常见需求。本文围绕 FastAPI 官方教程中的 Cookie 参数篇章(对应文档 docs/hi/docs/tutorial/cookie-params.md 及英文版 docs/en/docs/tutorial/cookie-params.md),完整讲解如何像声明 Query、Path 参数一样声明 Cookie 参数,并结合仓库内 fastapi/params.py、fastapi/param_functions.py 等源码解释其底层原理。读完本文,你将掌握 Cookie() 的正确导入与声明姿势、可选的默认值与校验参数、为何必须显式使用 Cookie,以及浏览器与 Swagger UI 对 Cookie 的特殊行为。
为什么需要声明式地读取 Cookie
在 HTTP 协议中,客户端(通常是浏览器)会通过 Cookie 请求头把一系列 key=value 键值对随请求发送给服务端。在没有声明式框架辅助时,开发者需要手动从 Request 对象中解析请求头、按分隔符拆分再逐个取值——繁琐且容易出错。
FastAPI 提供的思路非常直接:Cookie 参数的声明方式与查询参数 Query、路径参数 Path 完全同构。只要把函数参数标注为 Cookie 类型,FastAPI 的依赖注入系统就会自动帮你完成 Cookie 的提取、类型转换与校验,你只需关注业务逻辑本身。
导入 Cookie
使用 Cookie 参数的第一步是从 fastapi 中导入 Cookie:
from typing import Annotated
from fastapi import Cookie, FastAPI
app = FastAPI()
对应官方教程示例文件为 docs_src/cookie_params/tutorial001_an_py310.py(基于 typing.Annotated 的写法),仓库同时提供不使用 Annotated 的等价版本 docs_src/cookie_params/tutorial001_py310.py。
声明 Cookie 参数
导入完成后,你就可以采用与 Path、Query 完全相同的结构来声明 Cookie 参数。
方式一:Annotated 写法(推荐)
@app.get("/items/")
async def read_items(ads_id: Annotated[str | None, Cookie()] = None):
return {"ads_id": ads_id}
方式二:默认值写法
from fastapi import Cookie, FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items(ads_id: str | None = Cookie(default=None)):
return {"ads_id": ads_id}
在上面的示例中,我们声明了一个可选的 Cookie 参数 ads_id,其类型为 str | None,默认值为 None。当客户端请求 /items/ 并携带 Cookie: ads_id=xxx 时,函数就能拿到这个值;未携带时则得到 None,不会触发 422 校验错误。
在声明中加入默认值与校验
Cookie 参数同样支持「默认值 + 全部额外的校验与注解参数」的组合。例如:
from typing import Annotated
from fastapi import Cookie, FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items(
session_id: Annotated[
str,
Cookie(
default=None,
max_length=64,
description="客户端会话标识",
alias="session_id",
),
] = None,
):
return {"session_id": session_id}
从上文 fastapi/param_functions.py 中的函数签名可以看到,Cookie() 支持非常完整的参数集合,其中包括:
| 参数 | 作用 |
|---|---|
default |
参数未设置时的默认值 |
default_factory |
用于生成默认值的可调用对象 |
alias / validation_alias / serialization_alias |
为参数指定别名(可用于 Python 保留字等无法直接用作变量名的场景),影响取值与 OpenAPI 生成 |
title / description |
参数的展示标题与说明,会进入 OpenAPI schema |
gt / ge / lt / le |
数值范围校验(大于 / 大于等于 / 小于 / 小于等于) |
min_length / max_length |
字符串长度下限 / 上限 |
pattern |
字符串正则校验(regex 已弃用,请改用 pattern) |
strict |
是否启用严格模式(关闭隐式类型转换) |
multiple_of / max_digits / decimal_places |
面向数值的额外校验(倍数、最大位数、小数位数) |
deprecated |
在 OpenAPI 文档中标记该参数已弃用 |
include_in_schema |
是否把该参数包含进 OpenAPI schema |
json_schema_extra |
向 JSON Schema 注入额外字段 |
examples / openapi_examples |
为参数补充文档示例(example 已随 OpenAPI 3.1 弃用) |
从类定义来看,上述能力并非 Cookie 独有——见 fastapi/params.py,class Cookie(Param) 只是把 in_ 设置为 ParamTypes.cookie,再原样把全套字段校验参数透传给父类 Param。因此 Cookie 参数能够获得与 Query、Path 一致的开箱即用的数据校验、序列化与文档生成体验。
技术细节:Cookie、Path、Query 的姊妹关系
官方文档特别强调了一个技术细节:Cookie 是 Path 和 Query 的「姊妹类(sister class)」,它们共同继承自同一个 Param 基类。
但还有一个更隐蔽的知识点值得注意:当你 from fastapi import Cookie, Path, Query 时,导入的这几个名字实际上是函数,这些函数会返回真正参与参数解析的特殊类实例。以仓库源码为例,fastapi/param_functions.py 中定义的是 def Cookie(...),其函数体最终在 fastapi/param_functions.py 处执行 return params.Cookie(...);而真正的类定义 class Cookie(Param) 位于 fastapi/params.py,它通过 in_ = ParamTypes.cookie 来标记自己属于「Cookie 来源」的参数。
这条调用链在依赖求解阶段如何被消费?在 fastapi/dependencies/utils.py 的 add_param_to_fields() 中,FastAPI 依据 field_info.in_ 的值进行分派:ParamTypes.path 归入 path_params,ParamTypes.query 归入 query_params,ParamTypes.header 归入 header_params,其余情况下断言其必须为 ParamTypes.cookie 并归入 dependant.cookie_params。这正是「Cookie 参数走独立解析通道、不会与查询参数混淆」的实现基础。
重要提醒:必须用 Cookie,否则会被当作查询参数
一个新手极易踩坑的规则是:要声明 Cookie,必须使用 Cookie()。
原因在于,FastAPI 对普通函数参数有一套默认推断逻辑——当一个参数没有用 Path、Query、Cookie、Header、Body 等显式声明,且又不属于路径模板中的变量时,它默认会被解释成查询参数 Query。所以如果你写:
async def read_items(ads_id: str | None = None): # ❌ 会被当作查询参数
...
FastAPI 会认为 ads_id 是一个可选的 query 参数,而不是从请求 Cookie 中读取。只有显式标注 Cookie() 或 Annotated[..., Cookie()],取值来源才会被修正为 Cookie。
浏览器、JavaScript 与 Cookie:Swagger UI 里的特殊情况
Cookie 由浏览器在幕后以特殊方式管理,普通网页中的 JavaScript 并不能轻易直接读取或设置它们(这与 URL 查询参数完全不同)。
这一点直接影响了你在 /docs(Swagger UI)中的调试体验:
- 在 API 文档 UI 中,你确实可以看到每个 path operation 的 Cookie 参数文档说明——FastAPI 会根据声明自动把
ads_id这类参数渲染到 OpenAPI 页面中,其位置标记为in: cookie; - 但是,即使你在 UI 里填写了数据并点击 "Execute",由于该文档 UI 本身是用 JavaScript 运行的,请求发出时并不会携带你填写的 Cookie;
- 最终你会看到一个形如「好像根本没填任何值」的 error 提示(例如缺少必填 Cookie 值时的 422 校验错误)。
这不是代码 bug,而是浏览器安全模型 + UI 运行机制共同决定的预期行为。想真正调试带 Cookie 的接口,请使用支持设置 Cookie 的 HTTP 客户端(如 curl 的 -b "ads_id=xxx"、Postman 或 FastAPI 自带的 TestClient)。
用仓库测试验证完整行为
仓库为教程配套了完整的测试,见 tests/test_tutorial/test_cookie_params/test_tutorial001.py。该测试覆盖了四种典型场景:
("/items", None, 200, {"ads_id": None}), # 无任何 Cookie
("/items", {"ads_id": "ads_track"}, 200, {"ads_id": "ads_track"}), # 命中目标 Cookie
("/items", {"ads_id": "ads_track", "session": "cookiesession"}, 200, {"ads_id": "ads_track"}), # 忽略无关 Cookie
("/items", {"session": "cookiesession"}, 200, {"ads_id": None}), # 只传无关 Cookie
它通过 TestClient(app, cookies=cookies) 注入请求 Cookie,验证了:目标 Cookie 能被正确提取、无关 Cookie 会被安全忽略、缺省时返回默认值 None 且状态码保持 200。测试还额外断言了 /openapi.json 中生成的参数定义,其中关键字段为:
{
"name": "ads_id",
"in": "cookie",
"required": false,
"schema": {"anyOf": [{"type": "string"}, {"type": "null"}]}
}
注意这里的 "in": "cookie"——它直观地证实了 Cookie 参数会被正确路由到 OpenAPI 的 cookie 位置,而非 query 或 header,与源码中 in_ = ParamTypes.cookie 的分类结果完全一致。
小结:与 Query / Path 保持一致的心智模型
回顾全文,Cookie 参数的声明并不需要新的学习成本,只需沿用 Query 和 Path 的同一套通用模式:
- 从
fastapi导入Cookie(它实际是返回params.Cookie特殊类实例的函数); - 在函数签名中用
Cookie()标注参数,必要时借助Annotated组合类型与默认值; - 可选地叠加
max_length、pattern、alias、description、deprecated等校验与文档参数; - 记住「不加
Cookie就会被当作 query 参数」,以及「Swagger UI 无法真正发送 Cookie,需用专业客户端调试」。
如需继续深入,可对比阅读同目录下的 查询参数教程 与 路径参数教程,你会发现 Cookie、Header、Query、Path 在 FastAPI 中遵循完全统一的设计哲学。
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