FastAPI 通用实战配方:响应数据过滤与性能优化、OpenAPI 文档定制与 JSON 兼容编码
本文基于 FastAPI 官方文档的 General How-To Recipes 章节,把这份“通用问题速查”中的 10 个高频主题逐一展开:如何通过响应模型过滤敏感数据、如何利用 Pydantic 的 Rust 序列化提升 JSON 性能、如何为 path operations 配置 Tags / Summary / Description / 弃用标记、如何用 jsonable_encoder 把任意对象转换为 JSON 兼容结构、以及如何定制 OpenAPI 元数据与文档 URL。读完后,你能为项目中的每个 path operation 直接套用一套经过仓库示例与源码印证过的配置配方。
General Recipes 的定位
FastAPI 文档中的 How To - Recipes 章节收录的是若干相互独立的“配方”,大多数情况下你只需要在问题直接命中你的项目时才去查阅。本篇对应的 General 页面正是其中的“综合速查表”,它把若干通用或高频问题指向文档中的对应章节。下文按原文档的条目顺序,将每个配方补全为可复制的代码示例,并结合仓库内的示例源码(docs_src/ 目录)与核心实现(fastapi/ 目录)给出佐证。
配方 1:过滤响应数据——安全底线
问题:如何确保 API 不会返回比预期更多的数据?
做法:使用返回类型注解(return type)或 response_model 声明响应模型,FastAPI 会把输出数据限制并过滤到声明的字段范围内。官方指向 Response Model - Return Type 章节。
一个典型的“输入模型含密码、输出模型不含密码”的例子来自 示例源码:
from typing import Any
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: str | None = None
class UserOut(BaseModel):
username: str
email: EmailStr
full_name: str | None = None
@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
return user
即使 path operation 函数 实际 return user 返回了带密码的 UserIn 实例,FastAPI 也会用 response_model=UserOut 过滤掉 password 字段。原文档强调这是安全层面的关键能力:如果同一个模型被用在返回用户信息的其他接口上,就可能把用户的密码发送给每个客户端——官方文档明确警告,除非你清楚所有风险,否则永远不要以这种方式存储或返回明文密码。
配方 2:优化 JSON 响应性能
问题:返回 JSON 数据时如何优化性能?
做法:使用返回类型或响应模型。此时 Pydantic 会在 Rust 侧完成到 JSON 的序列化,无需经过 Python 层,因此会快得多(详见 Response Model 文档)。这一点与配方 1 是同一机制的两个收益:既保证了输出的安全过滤,又获得了高性能的序列化路径。
与之配套的还有几个常用变体(同样出自 Response Model 文档,示例源码在 docs_src/response_model/ 目录):
- 同时声明返回类型与
response_model时,response_model优先;可以把函数返回类型标为Any以通过 mypy 等严格类型检查,同时让 FastAPI 用response_model完成文档、验证与过滤; - 用
response_model=None可以禁用某个 path operation 的响应模型生成,例如你返回的是数据库对象或Response与dict的 union 等不是有效 Pydantic 字段的类型; response_model_exclude_unset=True/response_model_exclude_defaults=True/response_model_exclude_none=True用于省略默认值、只输出实际设置的字段(适合字段很多的 NoSQL 模型,避免返回冗长的全默认值 JSON);response_model_include/response_model_exclude接收set[str]字段名集合,做快速的字段级取舍——但官方提示,此时 OpenAPI 中的 JSON Schema 仍会是完整模型的 Schema,因此更推荐使用多个 Pydantic 类(如UserIn/UserOut)来表达输入输出的差异。
配方 3:为 API 添加文档 Tags——OpenAPI
问题:如何给 path operations 打标签并在文档 UI 中分组?
做法:给 path operation 装饰器 传 tags 参数,取值为 list[str](通常只是一个 str)。官方指向 Path Operation Configurations - Tags。
示例源码 展示了按资源分组的最常见写法:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
@app.post("/items/", tags=["items"])
async def create_item(item: Item) -> Item:
return item
@app.get("/items/", tags=["items"])
async def read_items():
return [{"name": "Foo", "price": 42}]
@app.get("/users/", tags=["users"])
async def read_users():
return [{"username": "johndoe"}]
这些 tags 会被写入 OpenAPI schema,并被自动生成的文档界面用于分组展示。应用较大时容易积累大量 tags,官方还建议把 tag 存入 Enum 以保证相关接口始终使用同一个标签名(见 Tags with Enums 小节,示例 tutorial002b_py310.py)。另外别忘了:这些参数是传给装饰器的(@app.get(...) 等),而不是你的 path operation 函数。
配方 4:Summary 与 Description——OpenAPI
问题:如何给 path operations 添加摘要和描述,并显示在文档 UI 中?
做法:给装饰器传 summary 与 description 参数。示例来自 示例源码:
@app.post(
"/items/",
summary="Create an item",
description="Create an item with all the information, name, description, price, tax and a set of unique tags",
)
async def create_item(item: Item) -> Item:
return item
补充一个官方文档中的进阶技巧:由于描述往往较长,可以写在函数的 docstring 里,FastAPI 会自动读取,并且支持在其中书写 Markdown(详见 Description from docstring 小节)。
配方 5:Response Description——OpenAPI
问题:如何定义文档 UI 中显示的“响应描述”?
做法:使用装饰器参数 response_description。注意区分:response_description 特指响应的描述,而 description 指的是 path operation 整体的描述。OpenAPI 规范要求每个 path operation 必须有响应描述,如果你不提供,FastAPI 会自动生成一句 "Successful response"(详见 Response description 小节,示例 tutorial005_py310.py)。
配方 6:弃用(Deprecate)一个 path operation
问题:如何标记某个 path operation 为已弃用但暂不删除?
做法:给装饰器传 deprecated=True。它会在交互式文档中被明确标记为弃用。示例源码 中 /elements/ 接口与普通的 /items/、/users/ 形成对照:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/", tags=["items"])
async def read_items():
return [{"name": "Foo", "price": 42}]
@app.get("/users/", tags=["users"])
async def read_users():
return [{"username": "johndoe"}]
@app.get("/elements/", tags=["items"], deprecated=True)
async def read_elements():
return [{"item_id": "Foo"}]
详见 Deprecate a path operation 小节。
配方 7:把任意数据转换为 JSON 兼容结构
问题:如何把任意数据类型(如 Pydantic 模型)转换成 JSON 兼容的结构(如 dict、list),例如存入只接受 JSON 兼容数据的数据库?
做法:使用 FastAPI 提供的 jsonable_encoder() 函数。官方指向 JSON Compatible Encoder 章节。
典型场景是数据库只接受 JSON 兼容数据:datetime 对象不兼容 JSON,需要转成 ISO 格式的 str;Pydantic 模型(带属性的对象)也不行,只能收 dict。示例源码 完整演示:
from datetime import datetime
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
fake_db = {}
class Item(BaseModel):
title: str
timestamp: datetime
description: str | None = None
app = FastAPI()
@app.put("/items/{id}")
def update_item(id: str, item: Item):
json_compatible_item_data = jsonable_encoder(item)
fake_db[id] = json_compatible_item_data
jsonable_encoder 接收一个对象(如 Pydantic 模型),返回其 JSON 兼容版本:把模型转成 dict、把 datetime 转成 str。需要特别注意它的返回不是一个包含 JSON 文本的大字符串,而是可以用 Python 标准库 json.dumps() 编码的普通数据结构。
从源码层面看,该函数定义在 fastapi/encoders.py#L129。官方文档还特别指出:jsonable_encoder 实际上被 FastAPI 内部用于数据转换——你在响应序列化等场景中享受到的类型兼容能力,底层就来自这一机制,而把它单独暴露出来正是为了让数据库存储等非路由场景也能复用。
配方 8:OpenAPI 元数据——文档信息
问题:如何给 OpenAPI schema 添加 API 标题、摘要、描述、版本、服务条款、联系人、许可证等元数据?
做法:在 FastAPI() 构造时设置相应字段,它们会用于 OpenAPI 规范与自动 API 文档 UI。官方指向 Metadata and Docs URLs 章节。参数说明如下:
| 参数 | 类型 | 说明 |
|---|---|---|
title |
str |
API 标题。 |
summary |
str |
API 的简短摘要。OpenAPI 3.1.0 / FastAPI 0.99.0 起可用 |
description |
str |
API 的简短描述,支持 Markdown。 |
version |
str |
API 的版本,是你自己应用的版本而非 OpenAPI 的版本,例如 2.5.0。 |
terms_of_service |
str |
API 服务条款 URL,若提供必须是 URL。 |
contact |
dict |
联系人信息,可含 name(联系人/组织名称)、url(URL 格式)、email(邮箱格式)三个字段。 |
license_info |
dict |
许可证信息,可含 name(设置 license_info 时必填)、url(许可证 URL)、identifier(SPDX 许可证表达式,与 url 互斥;OpenAPI 3.1.0 / FastAPI 0.99.0 起可用)。 |
完整示例:
from fastapi import FastAPI
description = """
ChimichangApp API helps you do awesome stuff. 🚀
## Items
You can **read items**.
## Users
You will be able to:
* **Create users** (_not implemented_).
* **Read users** (_not implemented_).
"""
app = FastAPI(
title="ChimichangApp",
description=description,
summary="Deadpool's favorite app. Nuff said.",
version="0.0.1",
terms_of_service="http://example.com/terms/",
contact={
"name": "Deadpoolio the Amazing",
"url": "http://x-force.example.com/contact/",
"email": "dp@x-force.example.com",
},
license_info={
"name": "Apache 2.0",
"url": "https://www.apache.org/licenses/LICENSE-2.0.html",
},
)
两个补充点:
description字段中可以直接写 Markdown,会在文档输出中被渲染(如上例中的加粗与斜体);- 自 OpenAPI 3.1.0 与 FastAPI 0.99.0 起,
license_info还可以用identifier(SPDX 表达式)代替url,参见 License identifier 小节与 示例源码。
同章节还介绍了 openapi_tags 参数:为一个列表,每个元素是对应一个 tag 的字典,可包含 name(必填,须与 path operations / APIRouter 中 tags 参数里的标签名一致)、description(支持 Markdown,会显示在文档 UI 中)、externalDocs(含 description 与必填的 url)。元数据字典在列表中的顺序决定标签在文档 UI 中的显示顺序——不要求为所有 tag 都添加元数据(见 示例源码)。
配方 9:自定义 OpenAPI URL(或移除)
问题:如何自定义 OpenAPI schema 的服务地址,或彻底关闭它?
做法:默认 OpenAPI schema 服务在 /openapi.json,可用 FastAPI() 的 openapi_url 参数定制。示例源码:
from fastapi import FastAPI
app = FastAPI(openapi_url="/api/v1/openapi.json")
@app.get("/items/")
async def read_items():
return [{"name": "Foo"}]
若要完全禁用 OpenAPI schema,可设置 openapi_url=None——这会同时禁用依赖它的文档用户界面(Swagger UI 与 ReDoc)。这些参数由 FastAPI 类在 fastapi/applications.py 中接收并用于构建 OpenAPI 路由与文档路由。
配方 10:自定义文档界面 URL
问题:如何修改自动生成的文档用户界面所用的 URL?
做法:两个内置文档界面分别是:
- Swagger UI:默认服务在
/docs,用docs_url参数设置 URL,设docs_url=None可禁用; - ReDoc:默认服务在
/redoc,用redoc_url参数设置 URL,设redoc_url=None可禁用。
示例源码 把 Swagger UI 改到 /documentation 并禁用 ReDoc:
from fastapi import FastAPI
app = FastAPI(docs_url="/documentation", redoc_url=None)
@app.get("/items/")
async def read_items():
return [{"name": "Foo"}]
快速索引:General Recipes 全条目一览
| 通用问题 | 对应配方 | 详细文档 |
|---|---|---|
| 过滤数据、防止返回敏感信息(安全) | 返回类型 / response_model |
Response Model - Return Type |
| 优化返回 JSON 的性能 | 返回类型 / 响应模型(Pydantic Rust 序列化) | Response Model - Return Type |
| 文档 Tags 分组 | tags 装饰器参数 |
Path Operation Configuration - Tags |
| Summary 与 Description | summary / description 参数(或 docstring) |
Path Operation Configuration - Summary and Description |
| 响应描述 | response_description 参数 |
Path Operation Configuration - Response description |
| 弃用 path operation | deprecated=True 参数 |
Path Operation Configuration - Deprecation |
| 任意数据转 JSON 兼容 | jsonable_encoder() |
JSON Compatible Encoder |
| OpenAPI 元数据(license、version、contact 等) | FastAPI() 元数据参数 |
Metadata and Docs URLs |
| 自定义 / 关闭 OpenAPI URL | openapi_url(None 可关闭) |
Metadata and Docs URLs - OpenAPI URL |
| 自定义文档界面 URL | docs_url / redoc_url |
Metadata and Docs URLs - Docs URLs |
以上配方均来自仓库内真实可运行的示例代码(docs_src/ 下对应文件)与官方教程文档,可直接复制到你的项目中按需裁剪。如需结构化地学习 FastAPI,建议按 Tutorial - User Guide 逐章阅读,而把本文当作日常开发中的速查手册。
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