FastAPI 响应 Cookie 实战:用 Response 参数与直接返回 Response 设置 Set-Cookie
本文基于 FastAPI 官方文档《Response Cookies》(docs/de/docs/advanced/response-cookies.md),讲解在 API 响应中设置 Cookie 的两种标准做法:在路径操作函数中声明 Response 参数写入 Cookie,以及直接构造并返回一个 Response 对象。结合仓库源码,还会说明 FastAPI 内部如何把"临时响应"上的 Cookie、Header 与状态码合并进最终响应,帮助你在生产接口中正确、安全地管理会话 Cookie。
方式一:声明 Response 参数(推荐)
你可以直接在路径操作函数中声明一个类型为 Response 的参数,这个对象是一个临时响应(temporary response),你可以在其中调用 set_cookie() 设置 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"}
完整可运行示例见 docs_src/response_cookies/tutorial002_py310.py。
设置完 Cookie 后,函数仍可以像平常一样返回任意对象(dict、数据库模型等);如果声明了 response_model,它依然会用于过滤和转换你返回的对象。
FastAPI 的工作机制是:把你在临时 Response 上设置的 Cookies(以及 Headers、状态码)提取出来,合并进最终响应——最终响应的 body 则是经过 response_model 过滤后、你实际返回值序列化得到的内容。
另外,Response 参数同样可以在依赖项(dependency)函数中声明,从而在依赖里集中设置 Cookie 和 Header,这对"登录依赖自动下发会话 Cookie"这类横切逻辑非常实用。
源码印证:临时 Response 从哪里来、如何被合并
从源码结构看,这条机制可以在仓库中完整追踪到:
-
临时 Response 的创建:在依赖求解阶段,solve_dependencies 在外部未传入
response时会新建一个空Response(),并主动删除其content-length头、把status_code置为None(见 fastapi/dependencies/utils.py)。这个对象会被注入进路径操作函数(或依赖函数)的Response形参,同时挂在SolvedDependency.response字段上(fastapi/dependencies/utils.py),最终随solved_result传递到路由处理层。 -
最终响应的组装:路由层的 get_request_handler 在执行完端点函数后,对非 Response 返回值先走
serialize_response()按response_model过滤序列化,然后执行关键的一行:response.headers.raw.extend(solved_result.response.headers.raw)(fastapi/routing.py)——临时响应上累积的set-cookie头正是通过这里被原样合并进最终响应。 -
状态码优先级:
_build_response_args显示,若临时响应上被设置了状态码,它会覆盖装饰器里声明的默认状态码,因此你可以在依赖或端点里用response.status_code = 201动态调整响应状态。
测试用例 tests/test_tutorial/test_response_cookies/test_tutorial002.py 验证了该用法:POST 请求后断言 response.cookies["fakesession"] == "fake-cookie-session-value" 且 body 仍是普通 JSON 对象,证明 Cookie 与 body 序列化互不干扰。
方式二:直接返回一个 Response 对象
你也可以在代码中直接构造一个 Response 对象,设置 Cookie 后原样返回。做法是先按"直接返回 Response"的常规方式创建响应(可参考 docs_src/response_directly/tutorial001_py310.py 的写法),再对其实例调用 set_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
完整示例见 docs_src/response_cookies/tutorial001_py310.py。
注意:直接返回 Response 会跳过 response_model 过滤
文档中特别提示(tip):如果你直接返回 Response 而不是使用 Response 参数,FastAPI 会原样直接返回它,不做任何 response_model 过滤。因此你必须自行保证:
- 数据是正确类型——例如返回
JSONResponse时,body 内容必须与 JSON 兼容; - 没有把本应被
response_model过滤掉的敏感数据(如内部字段)泄漏到响应中。
源码印证:get_request_handler 中有明确的分支——if isinstance(raw_response, Response) 时直接把返回值作为最终响应(仅在未设置后台任务时补上 solved_result.background_tasks),完全不经过 serialize_response() 的模型过滤流程。这也是两种写法在安全性上的本质区别,选择时应优先考虑 Response 参数方式。
更多说明:fastapi.Response 与 fastapi.responses 的来源
关于 Response 类的导入,文档给出了如下技术细节:
- 你也可以写
from starlette.responses import Response或from starlette.responses import JSONResponse,效果相同; - FastAPI 只是把
starlette.responses原样以fastapi.responses的名字再提供一遍,方便开发者;大部分可用的 Response 类实际上直接来自 Starlette; - 而因为
Response常被用来设置 Header 和 Cookie,FastAPI 还额外把它单独暴露为fastapi.Response,即 from .responses import Response as Response,所以你既可以从fastapi import Response,也可以从fastapi.responses import Response。
set_cookie() 的完整参数
set_cookie() 是 Starlette 提供的实例方法,除 key、value 外还支持 Cookie 规范中的常见属性:max_age(有效期秒数)、expires(过期时间)、path(作用路径)、domain(作用域)、secure(仅 HTTPS)、httponly(禁止 JavaScript 访问,防范 XSS 窃取会话)、samesite(lax/strict/none,防范 CSRF 场景)。完整参数与取值说明请查阅 Starlette 官方文档的 set-cookie 章节。
从会话安全实践角度,设置会话类 Cookie 时通常建议同时指定 httponly=True 与 secure=True,并配合 samesite="lax";FastAPI 本身只负责把 Cookie 头写进响应,策略由你在调用 set_cookie() 时决定。
小结与验证方式
两种方案的选择依据很清晰:
| 场景 | 推荐做法 | 关键文件 |
|---|---|---|
端点还要返回数据、且使用了 response_model |
声明 Response 参数 |
tutorial002_py310.py |
| 需要完全自定义响应体/状态码/Content-Type | 直接构造并返回 Response | tutorial001_py310.py |
| 在依赖中统一下发 Cookie(如会话、语言偏好) | 依赖函数中声明 Response 参数 |
fastapi/dependencies/utils.py |
本地验证方式:运行 uvicorn 启动对应 docs_src/response_cookies 示例,用 curl 发 POST 请求即可在响应头中看到 set-cookie: fakesession=fake-cookie-session-value; Path=/;或直接运行仓库自带的测试 tests/test_tutorial/test_response_cookies/test_tutorial001.py 与 test_tutorial002.py 观察断言行为。
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 StartedRust0623
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