FastAPI 响应 Cookie 设置指南:Response 参数注入与直接返回 Response 两种实战方案
在 FastAPI 中设置响应 Cookie 主要有两条官方推荐的实现路径:其一是在 path operation function(路径操作函数)或依赖中声明一个 Response 类型参数,把 Cookie「暂存」在由框架创建的临时响应对象上,随后照常返回任意业务对象;其二是直接构造并返回一个带 Cookie 的 Response(如 JSONResponse)。本文以仓库中的官方教程文档 docs/es/docs/advanced/response-cookies.md 为主体骨架,结合配套源码示例与测试用例,系统讲解这两种方案的写法、运行效果、与 response_model 的协作关系及底层实现细节。读完你将能够在自己的 FastAPI 应用中准确、安全地给客户端种下会话 Cookie。
两种方案与配套示例源码一览
仓库在 docs_src/response_cookies/ 目录下提供了与本文档一一对应的两个可运行示例:
| 方案 | 示例源码 | 路由路径 |
|---|---|---|
使用 Response 参数(临时响应对象) |
tutorial002_py310.py | POST /cookie-and-object/ |
直接返回 Response |
tutorial001_py310.py | POST /cookie/ |
两个文件都以 py310 命名,示例代码使用 def(同步)写法,语义上与 Python 3.10+ 环境下可直接运行;对应的自动化测试位于 tests/test_tutorial/test_response_cookies/。下面逐一展开。
方案一:声明 Response 参数设置 Cookie
在 path operation function 中声明一个 Response 类型的形参,随后调用该临时响应对象的 set_cookie() 方法即可:
from fastapi import FastAPI, Response
app = FastAPI()
@app.post("/cookie-and-object/")
def create_cookie(response: Response):
response.set_cookie(key="fakesession", value="fake-cookie-session-value")
return {"message": "Come to the dark side, we have cookies"}
对应仓库文件:tutorial002_py310.py。
这里的执行逻辑非常符合直觉:
- FastAPI 的依赖注入系统会为
response参数准备好一个临时的Response对象; - 你调用
response.set_cookie(key=..., value=...),把 Cookie 写入这个临时对象; - 函数最后仍像平时一样返回任意对象(
dict、数据库模型等),而无需手动组装 HTTP 响应; - FastAPI 会从该临时
Response中提取 Cookie(同样还有 Headers 与状态码),并入最终响应返回给客户端。
与 response_model 的协作
如果同时声明了 response_model,它仍然会对你的返回值执行过滤与类型转换。也就是说,返回的对象会先经过 response_model 的校验/序列化处理,而通过 Response 参数设置的 Cookie 独立于数据模型之外、原样保留在最终 HTTP 响应头中。这种「业务数据交给模型、传输控制交给 Response 参数」的职责划分,正是该方案相比直接返回 Response 的最大优势。
在依赖中设置 Cookie
同样的技巧也适用于依赖(Dependencies):你可以在依赖函数里声明 Response 参数并调用 set_cookie()(也可设置 Headers)。这样,凡是使用该依赖的路径操作都会自动附带统一的 Cookie——非常适用于集中式的会话初始化、埋点标识下发等横切场景。
方案二:直接返回带 Cookie 的 Response
另一种做法是放弃框架的返回序列化管线,直接在代码中创建 Response 实例、设置 Cookie 并返回它:
from fastapi import FastAPI
from fastapi.responses import JSONResponse
app = FastAPI()
@app.post("/cookie/")
def create_cookie():
content = {"message": "Come to the dark side, we have cookies"}
response = JSONResponse(content=content)
response.set_cookie(key="fakesession", value="fake-cookie-session-value")
return response
对应仓库文件:tutorial001_py310.py。
该方案的核心步骤是:
- 按 直接返回 Response 教程中的方式构造一个响应对象(此处使用
JSONResponse并传入content); - 调用
response.set_cookie(...)设置 Cookie; - 直接
return response。
⚠️ 直接返回时的注意事项
官方文档特别提醒:如果直接返回 Response,FastAPI 会原样返回它,不再走 response_model 的过滤与转换流程。因此你必须自己确保:
- 返回的数据类型正确。例如返回
JSONResponse时,content必须是 JSON 可序列化的; - 数据已经按预期准备好,不要遗漏任何「本应由
response_model帮你剔除」的敏感字段,因为此时模型过滤不会再起作用。
也就是说,方案二把数据的「生产与整形」责任完全交给了开发者,换取的是对最终响应头、状态码、Cookie 的全量直接控制。
两种方案的差异与选型建议
| 对比维度 | 方案一(Response 参数) |
方案二(直接返回 Response) |
|---|---|---|
| 返回值形态 | 可返回任意对象(dict、模型、ORM 对象等) |
必须返回 Response 实例本身 |
response_model |
仍生效,会自动过滤/转换返回数据 | 不生效,需手动保证数据形态正确 |
| Cookie/Headers/状态码 | 从临时响应对象提取后合并进最终响应 | 直接跟随该响应对象 |
| 适用场景 | 常规业务接口附带会话 Cookie | 需要完全自定义响应的场景 |
从源码看:fastapi.Response 与 starlette.responses 的关系
在 fastapi/responses.py 中可以看到,Response、JSONResponse、HTMLResponse、RedirectResponse、StreamingResponse、FileResponse 等几乎全部响应类,都是直接从 starlette.responses 重新导出(re-export)的:
from starlette.responses import FileResponse as FileResponse
from starlette.responses import JSONResponse as JSONResponse
from starlette.responses import Response as Response
...
也就是说:from fastapi.responses import Response 与 from starlette.responses import Response 指向的是同一个类,绝大多数可用的响应实现都源自 Starlette。FastAPI 之所以统一在其 fastapi.responses(以及 fastapi.Response)命名空间中再暴露一份,纯粹是出于开发者使用上的便利,避免你记忆两套导入来源。由于 Response 对象经常被用来设置 Headers 与 Cookies,FastAPI 因而特别提供了顶层可用的 fastapi.Response。
底层响应装配线索
从 fastapi/routing.py 的源码结构看,每个路径操作在内部都会被包装为一个最终返回 Response 对象的 app(request) 流程:代码先以 response: Response | None = None 初始化响应占位,再进入依赖解析与业务函数调用阶段——这也解释了为何注入的 Response 参数能代表「最终响应本身」:你在临时对象上设置的 Cookie、Headers 与状态码,最终都会被收集并装配到真正发回客户端的响应中。
一次可验证的完整运行
仓库在 tests/test_tutorial/test_response_cookies/ 中为两个示例各准备了一个集成测试,可以直接运行验证:
pytest tests/test_tutorial/test_response_cookies/
例如 test_tutorial002.py 的核心断言为:
from fastapi.testclient import TestClient
from docs_src.response_cookies.tutorial002_py310 import app
client = TestClient(app)
def test_path_operation():
response = client.post("/cookie-and-object/")
assert response.status_code == 200, response.text
assert response.json() == {"message": "Come to the dark side, we have cookies"}
assert response.cookies["fakesession"] == "fake-cookie-session-value"
它同时验证了两件事:接口正常返回 JSON 业务内容,且响应头中确实携带了名为 fakesession、值为 fake-cookie-session-value 的 Cookie。test_tutorial001.py 对方案二(直接返回 JSONResponse)给出了同样的断言,证明两种写法在 HTTP 层表现一致。
若想手动体验,也可将上述示例文件中的 app 交给本地开发服务器启动(例如以 uvicorn docs_src.response_cookies.tutorial002_py310:app --reload 的方式),再用浏览器开发者工具或 curl -v 观察响应中的 Set-Cookie 响应头。注意示例以 py310 命名,建议在 Python 3.10 及以上环境中运行。
更多 Cookie 属性:不止 key 与 value
示例代码只演示了最常用的 key 与 value 两个参数,实际上 Response.set_cookie() 还支持 Cookie 的各类标准属性,可覆盖绝大多数真实业务需求:
max_age:Cookie 的最大存活秒数;expires:过期时间(具体日期/时间);path:Cookie 生效的路径范围;domain:Cookie 生效的域名;secure:是否仅在 HTTPS 连接下传输;httponly:是否禁止 JavaScript 读取(抵御 XSS 的关键设置);samesite:跨站请求时是否携带 Cookie(如lax/strict/none)。
结合 FastAPI/Starlette 官方文档中关于 set_cookie 的参数说明(原文末尾亦提示读者可进一步查阅 Starlette 的响应文档)即可按需选用。在设置会话类 Cookie 时,建议至少考虑 httponly、secure 与 samesite 的组合,以降低 XSS、CSRF 等 Web 安全风险——这些属性属于标准的 HTTP Cookie 行为,与具体框架无关,但 FastAPI 的 Response.set_cookie() 都提供了直接的透传支持。
小结
回到本仓库的两份教程源码:想要「返回业务数据 + 顺手种下 Cookie」的常规接口,优先采用 tutorial002_py310.py 的 Response 参数方案,它保留了 response_model 的全部能力;当你需要完全掌控返回内容、或返回的本身就是某类 Response(重定向、流式、文件等)时,则参考 tutorial001_py310.py 直接构造响应并调用 set_cookie()。前者省心,后者彻底,配合 tests/test_tutorial/test_response_cookies/ 下的集成测试,你可以随时回归验证任意一种写法的正确性。
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 StartedRust0624
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