首页
/ FastAPI Cookie 参数详解:像声明 Query 一样优雅地读取 Cookie 请求参数

FastAPI Cookie 参数详解:像声明 Query 一样优雅地读取 Cookie 请求参数

2026-09-06 18:16:04作者:吴年前Myrtle

导读

在 Web 请求中,除路径参数与查询参数外,Cookie 是浏览器自动携带的又一类关键请求数据(会话标识、埋点追踪 ID、偏好设置等)。本篇教程聚焦 FastAPI 提供的 Cookie 参数声明机制,讲解如何在路径操作函数中读取并校验 Cookie,并结合本仓库源码说明其与 QueryPath 同源的实现原理,以及浏览器与 /docs 交互 UI 在传 Cookie 时的真实限制。学完本篇,你将掌握 Cookie 参数的标准声明方式、可选与校验用法、OpenAPI 文档呈现,以及如何用测试客户端验证 Cookie 读取逻辑。

完整示例:一个读取 Cookie 的最小接口

仓库中的配套示例 tutorial001_an_py310.py 给出了读取名为 ads_id 的 Cookie 的最小可运行代码:

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}

启动应用后,访问 http://127.0.0.1:8000/items/,并在请求中携带 Cookie: ads_id=some_value,接口就会读取该值并原样返回;若未携带任何 Cookie,则返回 {"ads_id": null}。示例同时提供了不使用 Annotated 的等价写法 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}

两种风格任选其一即可,仓库文档与代码以 Annotated 风格为主。

声明 Cookie 参数的两种风格

参考原文教程(cookie-params.md),定义 Cookie 参数的方式与定义 QueryPath 参数完全相同:

第一步:导入 Cookie,从 fastapi 中导入:

from fastapi import Cookie, FastAPI

第二步:在路径操作函数中声明。在参数中直接写 Cookie(),并给出 Python 类型注解。默认值可以是 None,也可以带上字符串长度、正则等额外校验与注解参数:

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}

其要点在于:

  • 函数参数 ads_id 的类型为 str | None,表示该 Cookie 可选,缺省时得到 None
  • 由于声明了默认值 None,参数非必填,OpenAPI 中会标记 "required": false
  • 返回值直接透传读取到的 Cookie 值,方便前端核对。

底层原理:CookieQueryPath 是同源的“姊妹类”

从源码结构看,原文所提示的“CookiePathQuery 的 sister 类”有清晰的代码依据。

params.py 中,四个参数类别统一由枚举 ParamTypes 表达:

class ParamTypes(Enum):
    query = "query"
    header = "header"
    path = "path"
    cookie = "cookie"

QueryPathHeaderCookie 四个类都继承自共同的基类 Param(其本身继承自 Pydantic 的 FieldInfo),每个子类只是通过类属性 in_ 标记自己的“归属位置”。Cookie 类的定义片段如下:

class Cookie(Param):  # type: ignore[misc]
    in_ = ParamTypes.cookie

正因如此,Cookie 可以复用 Param 基类提供的全部声明能力:默认值、别名、数值大小/字符串长度校验、正则、示例、说明文字等。

值得提醒的是,从 fastapi 导入的 CookieQueryPath 名称虽然是“类”,但实际导出的是 param_functions.py 中定义的同名函数——调用 Cookie() 时它内部会构造并返回一个 params.Cookie 实例。也就是说:

from fastapi import Cookie

导入的是函数 Cookie,调用 Cookie(default=None) 返回的是 params.Cookie 对象,类与函数同名共存、衔接透明。

数据流向在依赖解析层得到印证:dependencies/utils.pyget_dependant() 阶段依据 field_info_in 把字段分流进 cookie_params,随后在求解阶段调用:

cookie_values, cookie_errors = request_params_to_args(
    dependant.cookie_params, request.cookies
)

即 FastAPI 直接从 ASGI 请求对象的 request.cookies 中取出全部 Cookie,再按声明字段逐个匹配、类型转换与校验,最后把结果注入函数参数。

为什么必须用 Cookie() 显式声明

默认情况下,FastAPI 会按照“路径参数 → 查询参数 → 请求体(JSON 等)→ 表单/文件”的既定规则推断函数参数的来源。一个普通类型的参数(如 strint)如果不加任何 Cookie()/Query() 包装,会被解释为查询参数

因此,要声明 Cookie 必须显式使用 Cookie。一旦字段被标记为 cookie,OpenAPI 生成的参数位置 in 就是 "cookie",FastAPI 也会改从 request.cookies 中取值,而不是从 URL 查询串取值。

校验与扩展参数:默认值、长度、正则、别名等

Cookie 支持与 QueryPath 完全一致的“额外校验或注解参数”。可用的能力在 params.py 基类构造器中完整定义,常用项包括:

参数 作用 适用范围
default 字段缺省时的默认值 任意
default_factory 生成默认值的可调用对象 任意
alias 参数的备用名称(取值与 OpenAPI 均使用该名) 任意
description 参数的人类可读说明 任意
deprecated 在文档中标记该 Cookie 参数已弃用 任意
min_length / max_length 字符串最小/最大长度 字符串
pattern 字符串正则校验 字符串
gt / ge / lt / le 数值大于/大于等于/小于/小于等于 数值
multiple_of 数值倍数约束 数值
examples OpenAPI 示例 任意

注意这些数值与正则约束只有在类型匹配时才真正生效(例如给 int 类型的 Cookie 配 gt=0),在源码中它们会随字段元数据一并交给 Pydantic 进行编译与校验。

下面给一个综合示例:读取名为 session_id 的字符串 Cookie,要求长度介于 8 到 64 之间且必须是小写十六进制,同时附带说明与弃用标记:

from typing import Annotated

from fastapi import Cookie, FastAPI

app = FastAPI()


@app.get("/items/")
async def read_items(
    session_id: Annotated[
        str | None,
        Cookie(
            description="会话标识,8-64 位小写十六进制字符串",
            min_length=8,
            max_length=64,
            pattern="^[a-f0-9]+$",
            deprecated=True,
        ),
    ] = None,
):
    return {"session_id": session_id}

当请求携带的 Cookie 不满足约束时(例如长度不足或含非法字符),FastAPI 会返回带 422 Unprocessable Entity 的校验错误,而不是直接透传非法数据。

浏览器与 /docs 交互 UI 的真实限制

一个必须牢记的实践约束:浏览器会以特殊方式“幕后”管理 Cookie,通常不允许 JavaScript 轻易读写它们(尤其带有 HttpOnly 属性的会话 Cookie)。FastAPI 自带的交互式 API 文档位于 /docs,原文明确指出:你会在接口详情里看到 Cookie 参数的说明字段,但由于 Swagger UI 基于 JavaScript 运行,即使你在文档页填好 Cookie 值并点击 Execute,这些 Cookie 也不会被随请求发送,结果会表现为“好像什么都没填”的报错或空值。

因此实际联调 Cookie 相关接口时,更可靠的手段是:

  • 使用 curl / HTTPie 手动带上 -H "Cookie: ads_id=xxx"
  • 使用支持管理 Cookie 的 REST 客户端(如 Postman、Insomnia);
  • 使用 httpx / TestClient 在自动化测试中显式传入 cookies

用测试验证 Cookie 读取:TestClient 携带 Cookie

仓库配套测试 test_tutorial001.py 对上述示例做了完整覆盖,它同时参数化了两种声明风格,并断言四类场景:

  • 不带任何 Cookie 访问 /items200,响应 {"ads_id": null}
  • ads_id=ads_track200,响应 {"ads_id": "ads_track"}
  • 同时带 ads_id 与无关的 session Cookie → 只读取声明的 ads_id
  • 只带无关的 session Cookie → ads_idnull

测试通过 TestClient(mod.app, cookies=cookies) 注入 Cookie,等价于 HTTP 层发送 Cookie 请求头。你也可以在本地用 uvicorn 启动后手动验证:

uvicorn docs_src.cookie_params.tutorial001_an_py310:app --reload
curl -H "Cookie: ads_id=hello" http://127.0.0.1:8000/items/

Cookie 参数在 OpenAPI 文档中的呈现

由于 Cookie 参数在 OpenAPI 规范中被定义为独立的参数位置,FastAPI 会为声明的 Cookie 生成标准条目。根据测试中断言的 /openapi.json 快照,上述 ads_id 参数最终被生成为:

{
  "required": false,
  "schema": {
    "anyOf": [{"type": "string"}, {"type": "null"}],
    "title": "Ads Id"
  },
  "name": "ads_id",
  "in": "cookie"
}

可见其 in 字段为 "cookie",与查询参数("in": "query")、路径参数("in": "path")区分开。这一生成的入口位于 openapi/utils.py:它把依赖解析出的 cookie_params 与其他三类参数组合后统一序列化到 OpenAPI 的 parameters 列表中,因此基于 OpenAPI 的工具链(客户端生成器、API 网关、文档站点)都能正确识别 Cookie 参数。

延伸阅读与小结

如果你的 Cookie 数量较多,可以考虑用 Pydantic 模型批量声明,见教程 cookie-param-models.md;若要基于 Cookie 实现 API Key 认证,可直接使用 fastapi.security.APIKeyCookie,其底层同样是从 request.cookies 中按名字取值(见 api_key.py)。

本篇小结

  1. from fastapi import Cookie 导入,并用与 QueryPath 相同的方式在函数参数中声明;
  2. 支持默认值、可空类型以及长度、正则、别名等全部校验与注解参数;
  3. Cookie 在源码层面与 QueryPath 同为 Param 的子类,仅通过 in_ = ParamTypes.cookie 区分取值位置;
  4. 不写 Cookie() 的参数会被当成查询参数,声明时必须显式标记;
  5. 浏览器与基于 JavaScript 的 /docs UI 通常无法发送 Cookie,联调请改用 curl、REST 客户端或 TestClient 显式携带。
登录后查看全文
热门项目推荐
相关项目推荐