FastAPI 响应头设置完全指南:临时 Response 参数、直接返回与 CORS 暴露
本篇指南基于 FastAPI 官方文档《Response Headers》展开,讲解在 FastAPI 中设置响应头(Response Headers)的两种核心方式:通过声明 Response 参数操作临时响应对象,以及直接返回带 headers 的 Response 实例。读完本文,你将掌握这两种方式的适用场景、与 response_model 的协同关系,以及浏览器端读取自定义头所需的 CORS 配置。
两种设置响应头的方式概览
FastAPI 允许你在以下两个位置控制响应头:
| 方式 | 适用场景 | 示例代码位置 |
|---|---|---|
声明 Response 参数(临时响应) |
仍需要返回 dict、数据库模型等普通对象,或已声明 response_model 做过滤/转换时 |
docs_src/response_headers/tutorial002_py310.py |
直接返回 Response |
需要完全控制响应体与头,例如返回 JSON 字符串、自定义 Content-Language 等 |
docs_src/response_headers/tutorial001_py310.py |
两种方式可以同时出现在一个应用中,互不冲突。
方式一:使用 Response 参数(临时响应对象)
你可以在路径操作函数中声明一个类型为 Response 的参数(与设置 Cookie 的方式类似),然后在这个临时响应对象上设置响应头:
from fastapi import FastAPI, Response
app = FastAPI()
@app.get("/headers-and-object/")
def get_headers(response: Response):
response.headers["X-Cat-Dog"] = "alone in the world"
return {"message": "Hello World"}
(示例源码:docs_src/response_headers/tutorial002_py310.py)
设置完头之后,你只需按平时的方式返回任何你需要的对象即可——dict、数据库模型等都可以。
关键点:即使你声明了 response_model,它仍然会照常用于过滤和转换你返回的对象。临时响应对象只贡献“头信息”(响应头、Cookie、状态码),响应体完全由你的返回值和 response_model 决定。
FastAPI 会从该临时响应中提取头(也包括 Cookie 和状态码),并将它们合并进最终包含你返回值(经 response_model 过滤后)的响应中。
这个机制在源码中有明确的体现:
- 在 fastapi/dependencies/utils.py 的
solve_dependencies()中,当调用方没有传入 response 时,会创建一个临时Response(),并主动删除其中的content-length头、把status_code置为None,表明这个对象只用于承载头信息:
if response is None:
response = Response()
del response.headers["content-length"]
response.status_code = None
- 在 fastapi/routing.py 的路由处理函数中,路径操作函数返回普通对象时,FastAPI 先经过
serialize_response()(受response_model约束)生成响应,随后执行:
response.headers.raw.extend(solved_result.response.headers.raw)
这一行(约 L750)正是“从临时响应提取头、合并进最终响应”的实现:solved_result.response 就是传给路径操作函数的临时 Response。
在依赖中同样可用:你也可以在依赖函数中声明 Response 参数并设置头(和 Cookie),例如:
def my_dependency(response: Response):
response.headers["X-From-Dependency"] = "true"
依赖中设置的头会经过同一套依赖解析流程(solve_dependencies),最终一并合并到响应上。因此可以在全局/局部依赖中统一添加追踪 ID、版本号等公共响应头。
方式二:直接返回 Response
如果你要直接返回一个 Response 对象(参见文档 Return a Response Directly),也可以在构造时把 headers 作为额外参数传入:
from fastapi import FastAPI
from fastapi.responses import JSONResponse
app = FastAPI()
@app.get("/headers/")
def get_headers():
content = {"message": "Hello World"}
headers = {"X-Cat-Dog": "alone in the world", "Content-Language": "en-US"}
return JSONResponse(content=content, headers=headers)
(示例源码:docs_src/response_headers/tutorial001_py310.py)
此时 JSONResponse 的 headers 参数会原样生效。从源码可以看到,当路径操作函数的返回值 isinstance(raw_response, Response) 时(fastapi/routing.py 约 L711),FastAPI 会直接把它作为最终响应返回(仅在其没有 background 任务时补上 solved_result.background_tasks),不再走 response_model 过滤逻辑——这是与方式一的本质区别。
技术细节:fastapi.responses 与 starlette.responses
你也可以使用 from starlette.responses import Response 或 from starlette.responses import JSONResponse。
FastAPI 提供的 fastapi.responses 与 starlette.responses 中的类型基本是同一套,只是作为开发便利的再导出(re-export)。可以从 fastapi/responses.py 直接看到:
from starlette.responses import JSONResponse as JSONResponse # noqa
from starlette.responses import Response as Response # noqa
...
其中绝大多数响应类型(FileResponse、HTMLResponse、PlainTextResponse、RedirectResponse、StreamingResponse 等)直接来自 Starlette。而 Response 因为经常被用于设置响应头和 Cookie,FastAPI 还在 fastapi.Response 顶层也提供了它,方便直接 from fastapi import Response 导入。
自定义头与 CORS 暴露
使用自定义头时注意以下两点:
- 私有/自定义专有头可以使用
X-前缀命名(如示例中的X-Cat-Dog),避免与标准 HTTP 头冲突; - 让浏览器端 JavaScript 能读到自定义头:浏览器出于安全策略,默认只暴露
Content-Type、Set-Cookie等少数响应头给跨域请求的 JS 代码。如果你的自定义头需要被浏览器中的客户端看到,必须在 CORS 配置中加入它们——在 CORS (跨域资源共享) 的配置中使用 Starlette CORS 中间件文档中的expose_headers参数:
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
expose_headers=["X-Cat-Dog"],
)
行为验证:官方测试用例
上述两种用法在仓库测试中都有对应的断言,可以直接查看验证逻辑:
- tests/test_tutorial/test_response_headers/test_tutorial001.py:对直接返回
JSONResponse的/headers/路由断言response.headers["X-Cat-Dog"] == "alone in the world"; - tests/test_tutorial/test_response_headers/test_tutorial002.py:对声明
Response参数的/headers-and-object/路由做同样的断言,确认临时响应上的头确实被合并进了最终响应。
小结
- 需要保留
response_model过滤、只额外加头/Cookie/状态码 → 声明Response参数,在依赖或路径操作函数中写入response.headers; - 需要完全自定义响应体 → 直接返回
Response/JSONResponse并在构造参数中传headers; - 浏览器端要读自定义头 → 用 CORS 中间件的
expose_headers显式暴露; - 底层原理见 fastapi/dependencies/utils.py(临时
Response()的创建)与 fastapi/routing.py(response.headers.raw.extend(...)的头部合并)。
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