FastAPI Cookie 参数详解:用 Cookie 声明、验证与读取 HTTP Cookie
【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi
本文基于 FastAPI 官方教程文档 cookie-params(德语版,对应英文版为 docs/en/docs/tutorial/cookie-params.md)展开。FastAPI 允许像声明Query、Path参数一样声明 Cookie 参数:只需从fastapi导入Cookie,用相同的方式设置默认值、附加验证与标注参数,即可从请求 Cookie 中按名称提取并自动完成类型转换与校验。读完本文,你将掌握 Cookie 参数的声明写法、Cookie在框架中的类层级关系,以及为什么必须显式使用Cookie才能避免参数被误判为 Query 参数,并理解 Cookie 值在依赖解析阶段的底层提取流程。
1. 导入Cookie
使用 Cookie 参数前,第一步是从fastapi中导入Cookie。官方示例文件 docs_src/cookie_params/tutorial001_an_py310.py 的完整代码如下:
from typing import Annotated from fastapi import Cookie, FastAPI app = FastAPI() @app.get("/items/") async def read_items(ads_id: Annotated[str | None, Cookie()] = None): return {"ads_id": ads_id}其中关键的第 3 行from fastapi import Cookie, FastAPI即文档强调的导入步骤。Cookie与Query、Path一样,是 FastAPI 顶层命名空间直接暴露的 API。
2. 声明 Cookie 参数
声明 Cookie 参数使用与Path、Query完全相同的结构:你可以为参数定义默认值,以及所有的额外验证或标注参数。
2.1 现代推荐写法(Annotated风格)
@app.get("/items/") async def read_items(ads_id: Annotated[str | None, Cookie()] = None): return {"ads_id": ads_id}逐行解读:
Annotated[str | None, Cookie()]:第一个元素str | None声明参数类型——字符串或None;第二个元素Cookie()把该参数从默认的"Query 参数"语义转换为"从 Cookie 中提取"。= None:指定默认值。当请求中没有名为ads_id的 Cookie 时,参数值为None。- 由于参数类型包含
None,该 Cookie 是可选的;如果你声明为Annotated[str, Cookie()](不可为空),FastAPI 会在缺少该 Cookie 时返回验证错误。
2.2 传统写法(旧风格)
仓库中同时提供了旧风格示例 docs_src/cookie_params/tutorial001_py310.py:
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(default=None)把Cookie当作一个"参数工厂"直接用作默认值。两种写法最终生成同样的路由行为,官方文档与当前示例主推Annotated风格。
2.3 可附带的验证与标注参数
Cookie与Query、Path共用同一套参数选项,因此声明 Cookie 时同样可以使用:
- 元数据:
title、description、deprecated、examples/openapi_examples、include_in_schema等(影响/docs中生成的 OpenAPI 文档); - 验证约束:
gt、ge、lt、le(数值比较)、min_length、max_length、pattern(字符串正则)、strict、multiple_of、allow_inf_nan等; - 别名:
alias、validation_alias、serialization_alias,用于 Cookie 名称与 Python 变量名不一致(例如 Cookie 名与 Python 保留字冲突)的场景。
这些选项的完整签名可以在 fastapi/params.py 的Param基类与各参数类构造器中查证。
3. 技术细节:Cookie是Path与Query的"姐妹"类
官方文档特别指出:
Cookie是Path和Query的一个"姐妹"(sister)类。它也继承自同一个共同的Param类。但请记住,当你从
fastapi导入Query、Path、Cookie等对象时,它们实际上是函数,会返回特殊的类。
仓库源码完整印证了这两句话:
共同的
Param基类。在 fastapi/params.py 中:class Cookie(Param): # type: ignore[misc] in_ = ParamTypes.cookieCookie直接继承Param,并仅通过类属性in_ = ParamTypes.cookie声明自己的取值来源是 Cookie。这正是它与Query(in_ = ParamTypes.query)、Path(in_ = ParamTypes.path)成为"姐妹"类的机制——同一个基类、同一套验证参数,仅取值位置不同。顶层名字是"返回类的函数"。在 fastapi/param_functions.py 中,
Cookie被定义为一个带大量Doc文档标注的函数(第 1018 行def Cookie(...)),与同文件的Path(第 13 行)、Query(第 357 行)结构一致:从源码结构看,这些函数接收default、alias、title、description、各类验证约束等参数,并转发给params模块中对应的真实类(例如Path函数返回params.Path实例,Query函数返回params.Query实例,Cookie同理返回params.Cookie实例)。这种"函数 + 类"双层设计让 FastAPI 既能在Annotated中作为标注使用,又能在旧风格中作为"默认值"使用,同时把参数文档与 OpenAPI 生成所需信息集中维护在param_functions.py中。
4. 为什么必须用Cookie显式声明
文档给出了一条关键提示:
要声明 Cookie,你必须使用
Cookie,否则这些参数会被解释为 Query 参数。
这一点在依赖解析源码中有直接证据。fastapi/dependencies/utils.py 在处理非 Body 参数时,会根据Param实例的in_归属把字段分派到不同的参数列表,并做了严格断言:
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)也就是说:
- 没有标注任何
Param的简单类型参数(如裸的ads_id: str)会被归类为Query 参数,框架会去 URL 查询串里找?ads_id=...,而不是读 Cookie; - 只有标注了
Cookie(其in_为ParamTypes.cookie)的参数才会进入dependant.cookie_params列表,后续才从request.cookies中提取。
这正是文档强调"必须显式使用Cookie"的底层原因。
5. 注意:浏览器 Cookie 的特殊性与/docs界面限制
文档还提醒了一个容易被忽略的行为限制:
请记住,浏览器以特殊方式、在幕后处理 Cookie,并且不允许JavaScript随意地直接访问/修改它们。
当你访问
/docs的API 文档界面时,可以看到路径操作中 Cookie 的文档;但即使你填写了数据并点击"Execute",由于文档界面是基于JavaScript运行的,Cookie 并不会被发送,你会看到一条错误信息,仿佛你没有填写任何值。
也就是说:
/docs(Swagger UI)能正确展示 Cookie 参数的 OpenAPI 定义(参数位于cookie位置);- 但 Swagger UI 通过浏览器
fetch发请求时无法可靠地注入自定义 Cookie,点击 Execute 后服务端读不到该 Cookie,若参数必填则返回 422 验证错误,看起来就像"没有填值"。
这是浏览器 Cookie 机制(而非 FastAPI)造成的限制。实际测试 Cookie 参数时,建议使用curl、Postman 等可直接控制Cookie请求头的工具,或框架自带的TestClient(见下一节)。
6. 底层原理:Cookie 值是如何被提取的
结合 fastapi/dependencies/utils.py,完整解析流程可以概括为:
收集阶段:路由参数被解析后,所有
in_为cookie的字段被归入dependant.cookie_params(同文件第 173 行初始化cookie_params: list[ModelField],并在第 189-195 行完成依赖树的扁平化收集)。提取阶段:在生成请求参数值时,框架调用:
cookie_values, cookie_errors = request_params_to_args( dependant.cookie_params, request.cookies ) values.update(cookie_values) errors += path_errors + query_errors + header_errors + cookie_errors即把 ASGI/Starlette 解析好的
request.cookies字典(键为 Cookie 名,值为字符串)交给request_params_to_args,按字段定义完成取值、类型转换与验证;任何验证失败都会汇入errors,最终触发 422 响应。类型转换:Cookie 值本质都是字符串,FastAPI 依据你声明的类型(如
str、int)自动转换;转换或验证失败时,与 Query/Path 参数一样返回统一的验证错误结构。
从源码结构看,Cookie 参数的处理路径与 Query/Header 参数共用同一套request_params_to_args工具函数,只是数据源不同(Query 来自request.query_params,Cookie 来自request.cookies),这也解释了为什么Cookie能完整复用Query的全部验证与元数据参数。
7. 测试验证
官方为该教程配置了对应的测试 tests/test_tutorial/test_cookie_params/test_tutorial001.py,其参数化用例覆盖了三种典型场景:
- 请求携带
{"ads_id": "ads_track", "session": "cookiesession"}两个 Cookie 访问/items,期望返回200且响应为{"ads_id": "ads_track"}; - 请求只携带
{"session": "cookiesession"}(无ads_id)访问/items,由于参数有默认值None,期望返回200且响应为{"ads_id": None}; - 测试同时断言生成的 OpenAPI 中该参数的位置为
"in": "cookie",验证文档元数据正确。
测试通过TestClient(mod.app, cookies=cookies)以字典形式注入 Cookie,这也是本地验证 Cookie 参数最直接的方式。
8. 小结
- 声明 Cookie 使用
Cookie,模式与Query、Path完全一致:Annotated[str | None, Cookie()] = None或旧风格ads_id: str | None = Cookie(default=None); Cookie在 fastapi/params.py 中继承共同的Param基类,仅以in_ = ParamTypes.cookie标识取值来源;而从fastapi导入的Query/Path/Cookie等名字实际是 fastapi/param_functions.py 中返回特殊类实例的函数;- 必须显式标注
Cookie,否则参数会被当作 Query 参数处理(见 fastapi/dependencies/utils.py 的分派与断言逻辑); - 运行时 Cookie 值从
request.cookies提取并经request_params_to_args完成验证(见 fastapi/dependencies/utils.py); - 浏览器 JavaScript 无法随意操纵 Cookie,因此
/docs界面中点击 Execute 不会真正发送 Cookie,集成测试请改用TestClient或外部 HTTP 客户端。
【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考