首页
/ FastAPI 设置 Response 响应头(Headers)的完整指南:`Response` 参数、直接返回与自定义 Header

FastAPI 设置 Response 响应头(Headers)的完整指南:`Response` 参数、直接返回与自定义 Header

2026-09-06 19:26:31作者:秋阔奎Evelyn

**响应头(Response Headers)**是 HTTP 响应中承载元信息(语言、缓存策略、自定义业务标识等)的关键载体。本篇文章基于 FastAPI 官方高级用法文档 response-headers.md(源文档为多语言翻译版本之一,正文以英文原版 response-headers.md 为基准)整理而成,讲解在 FastAPI 中设置响应头的两种推荐方式,并结合仓库源码与测试用例说明其底层运行机制。读完本文,你将掌握:通过 Response 参数向"临时"响应对象写入头信息、在直接返回 Response 对象时附带 headers,以及如何让自定义 Header 在浏览器中被前端 JavaScript 读取。

概述:FastAPI 中设置响应头的两条技术路径

在 FastAPI 中,绝大多数场景下你并不直接构造 HTTP 响应——框架会根据你返回的对象(dict、模型等)自动完成序列化与响应构建。因此要"优雅地"给这类响应附加自定义 Header,官方提供了两种推荐写法,对应仓库 docs_src/response_headers 目录下的两个独立示例:

写法 代码示例 适用场景
声明 Response 参数并写入 headers tutorial002_py310.py 返回普通对象(dict、模型),同时附带自定义头
直接返回带 headers 的 Response tutorial001_py310.py 需要完全控制响应对象本身(含状态码、内容类型等)

下面依次展开两种方式的具体写法、组合规则与底层实现。

方式一:在路径操作函数中声明 Response 参数

FastAPI 允许你在 path operation function 中声明一个类型为 Response 的参数(这与操作 Cookie 的方式完全一致)。声明之后,你就可以向这个"临时"(temporary)的响应对象写入 header。

完整的官方示例位于 tutorial002_py310.py

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"}

关键点逐条拆解如下:

  • Response 来自哪里:这里的 Response 直接导入自 fastapi 顶层包。由于设置 headers 与 cookies 是高频操作,FastAPI 特意把 Response 暴露在 fastapi.Response 中供开发者直接使用(源码见 fastapi/init.pyfrom .responses import Response,而 fastapi/responses.py 又将其复导出自 starlette.responses)。
  • 返回值不受影响:在写入 header 之后,你依然可以像平常一样返回任意对象——dict、数据库模型等。响应体的序列化照常进行。
  • response_model 依旧生效:如果路径操作声明了 response_model,它仍然会用于对返回值进行过滤与类型转换,不会因为声明了 Response 参数而被绕过。
  • headers 会被"搬运"到最终响应:FastAPI 会从那个临时响应中提取 headers(同时还有 cookies 与状态码),把它们合并进携带了你返回值的最终响应中,再经由 response_model 过滤后发给客户端。

在真正返回 Response 对象(即"方式二")时,这一合并逻辑同样会执行:先从临时响应提取头信息并附加到最终响应之上,从而保证两种写法可以组合使用而不丢失任何 header。

在依赖项中声明 Response 参数

文档特别强调:Response 参数不仅可以用在路径操作函数中,也可以声明在依赖项(dependencies)里,并在依赖中设置 headers 与 cookies。这是实现"统一为一批接口附加公共头信息(如追踪 ID、公共响应头)"的推荐手段——依赖中写入的头信息同样会被合并进最终响应,因为整条依赖链共享同一个临时 Response 对象(机制详见下文源码分析)。

底层原理:临时 Response 的创建与头部合并

从源码结构可以还原出这一"魔法"的完整调用链,这也印证了文档中"temporary response"的说法:

  1. 创建临时响应:在 fastapi/dependencies/utils.pysolve_dependencies() 中,当依赖解析开始时若未传入外部 response,会创建一个全新的 Response() 实例:
if response is None:
    response = Response()
    del response.headers["content-length"]
    response.status_code = None  # type: ignore

这个对象一路向下传递给路径操作函数与所有依赖,因此你在函数或依赖里通过 response.headers[...] = ... 写入的内容,最终都会累积在这个临时对象上。

  1. 合并进最终响应:在处理完你的返回值之后,fastapi/routing.py(及其后针对不同返回分支的多处代码,如 L682、L704、L750)执行了关键的头部拼接:
response.headers.raw.extend(solved_result.response.headers.raw)

solved_result.response.headers.raw 正是临时 Response 上累积的全部 header 原始键值对,extend 把它们原样追加到最终响应上——这就是"文档里设置的 headers 最终出现在 HTTP 响应头中"的直接代码依据。

测试验证

仓库提供了针对上述示例的端到端测试,见 test_tutorial002.py

def test_path_operation():
    response = client.get("/headers-and-object/")
    assert response.status_code == 200, response.text
    assert response.json() == {"message": "Hello World"}
    assert response.headers["X-Cat-Dog"] == "alone in the world"

测试同时断言了响应体({"message": "Hello World"})与自定义头 X-Cat-Dog 都正确返回,完整验证了"既能设置 header、又能正常返回序列化对象"的预期行为。运行该测试可执行:

pytest tests/test_tutorial/test_response_headers/

方式二:直接返回一个携带 headers 的 Response

另一种更直接的做法是:当你本来就要直接返回一个 Response 对象时,把 headers 作为构造参数一并传入。完整示例见 tutorial001_py310.py

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)

这里的关键是:

  • 按文档 response-directly.md("直接返回 Response"专题)描述的方式构造响应,再把 headers 作为额外参数传入响应类的构造函数;
  • 示例中一次性传入了两个头:自定义的 X-Cat-Dog 以及标准的 Content-Language: en-US(用于声明内容语言);
  • 任意合法的 Response 子类(JSONResponseHTMLResponsePlainTextResponse 等)都支持 headers 参数。

对应的测试 test_tutorial001.py 断言了响应体及两个 header 均正确返回:

def test_path_operation():
    response = client.get("/headers/")
    assert response.status_code == 200, response.text
    assert response.json() == {"message": "Hello World"}
    assert response.headers["X-Cat-Dog"] == "alone in the world"
    assert response.headers["Content-Language"] == "en-US"

技术细节:fastapi.responsesstarlette.responses 的关系

文档中的"Technical Details"提示明确说明了一个易混淆点:

  • 你完全可以直接写 from starlette.responses import Responsefrom starlette.responses import JSONResponse
  • FastAPI 之所以额外提供 fastapi.responses,纯粹是为了方便开发者——其中绝大多数 Response 类都直接来自 Starlette(见 fastapi/responses.pyfrom starlette.responses import Response as Response 的复导出);
  • 由于 Response 常被用于设置 headers 与 cookies,FastAPI 也特意将其暴露在 fastapi.Response

换言之,两种导入路径等价,选择哪种只取决于你的代码风格偏好。本文两个示例恰好分别示范了这两种导入方式:方式一用 fastapi.Response,方式二用 fastapi.responses.JSONResponse

自定义 Headers 与浏览器可见性(CORS expose_headers

自定义业务头(Custom Headers)是响应头最常见的应用之一,例如上例中的 X-Cat-Dog。需要了解两条实践规则:

  1. 命名约定:自定义的专有 Header 习惯上使用 X- 前缀命名(这一约定源自 HTTP 头字段的通用实践)。示例中的 X-Cat-DogX-* 系列即为此类。若头名是标准头(如 Content-LanguageCache-Control),则直接使用标准名称。

  2. 浏览器可见性(关键陷阱):如果你设置了自定义 Header,并希望浏览器中的前端 JavaScript(如 fetchXMLHttpRequest)能够读取到它,仅仅设置 header 是不够的——还必须在 CORS(跨域资源共享) 配置中把该头加入 expose_headers 参数。否则即便后端确实返回了该头,浏览器也不会把自定义头暴露给页面脚本。

    FastAPI 中配置方式为使用 CORSMiddleware(由仓库复导出自 Starlette,见 fastapi/middleware/cors.py):

from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://example.com"],
    allow_methods=["*"],
    allow_headers=["*"],
    expose_headers=["X-Cat-Dog"],  # 关键:把自定义头暴露给浏览器
)

更完整的 CORS 配置(含 allow_originsallow_credentials 等参数的逐一说明)请参见本仓库的专题文档 CORS 指南(es) 与英文原版 CORS(en)

快速上手:运行与验证

在本地验证上述两种写法,只需将对应示例保存为 main.py 后用 Uvicorn 启动:

uvicorn main:app --reload
  • 访问 http://127.0.0.1:8000/headers/(方式二示例)可看到 JSON 响应体 {"message": "Hello World"}
  • 使用浏览器开发者工具或 curl -i 查看响应头,即可在 HTTP/1.1 200 OK 段落中看到 x-cat-dog: alone in the world(方式二还会附加 content-language: en-US)。

两条路径的取舍总结如下:

  • 需要"既自定义 header,又保留 FastAPI 自动序列化与 response_model 过滤能力"→ 选用方式一(Response 参数),它也是可在依赖项中复用、面向"给一批接口统一加头"场景的更优雅方案;
  • 需要完全掌控整个响应对象(自定义内容类型、状态码、渲染逻辑等)→ 选用方式二(直接返回 Response

无论选择哪种,都请牢记自定义 Header 的"最后一公里":若目标客户端是浏览器中的脚本,务必同步配置 CORS 的 expose_headers,否则这些头将无法被页面读取。

登录后查看全文
热门项目推荐
相关项目推荐