FastAPI: API 文档与 OpenAPI 定制 — 打造专业 API 文档

最后更新:2026-08-26

API 文档就像产品说明书——没有它,客户(开发者)不会用你的产品;写得不好,客户会用脚投票。自动生成文档是 FastAPI 的杀手锏,但默认配置只是起点,定制才是专业。

1. 你将学到


2. Alice 的真实故事

(1) 痛点:默认文档不够专业

Alice 的 PriceTracker API 文档是 FastAPI 默认的 Swagger UI,所有端点混在一起没有分组,Bob 找不到需要的端点。更糟的是,文档没有示例请求,Bob 不知道 currency 字段该填什么值,请求体和响应体没有版本区分,新端点和废弃端点混在一起。

(2) OpenAPI 定制的解法

FastAPI 允许深度定制 OpenAPI 文档:tags 分组、示例值、废弃标记、自定义 Schema——让 API 文档从"能看"变成"好用"。

(3) 收益

Bob 打开文档即可按标签(Products/Prices/Auth)快速定位端点,示例值让请求不再猜填,废弃端点灰色标记不会误用,文档质量从"内部参考"升级为"对外发布级"。


3. Swagger UI 与 ReDoc 配置

(1) OpenAPI 生成流程

100%
flowchart LR
    A[FastAPI Routes] --> B[Pydantic Models]
    B --> C[OpenAPI 3.1 Schema]
    C --> D[Swagger UI /docs]
    C --> E[ReDoc /redoc]
    C --> F[Raw JSON /openapi.json]

▶ 示例:FastAPI 应用级文档配置

PYTHON
from fastapi import FastAPI

app = FastAPI(
    title="PriceTracker API",
    description="""
    ## PriceTracker SaaS API
    
    E-commerce price tracking service for monitoring million-level product prices.
    
    ### Authentication
    All protected endpoints require a Bearer token obtained from `/auth/login`.
    
    ### Rate Limits
    - Free: 100 requests/min
    - Pro: 1000 requests/min
    - Enterprise: unlimited
    """,
    version="1.0.0",
    terms_of_service="https://pricetracker.example.com/terms",
    contact={
        "name": "PriceTracker Support",
        "url": "https://pricetracker.example.com/support",
        "email": "support@pricetracker.example.com",
    },
    license_info={
        "name": "MIT License",
        "url": "https://opensource.org/licenses/MIT",
    },
    docs_url="/docs",
    redoc_url="/redoc",
    openapi_url="/openapi.json",
)

输出:

TEXT 📖 仅展示
# 执行成功

▶ 示例:Swagger UI 参数定制

PYTHON
app = FastAPI(
    swagger_ui_parameters={
        "persistAuthorization": True,  # Keep auth between page refreshes
        "displayRequestDuration": True,  # Show request duration
        "filter": True,  # Enable search filter
        "syntaxHighlight.theme": "monokai",  # Code highlighting theme
        "defaultModelsExpandDepth": 1,  # Expand models 1 level deep
        "defaultModelExpandDepth": 1,
    }
)

输出:

TEXT 📖 仅展示
# 执行成功
参数 默认 说明
persistAuthorization False 刷新页面保留认证
displayRequestDuration False 显示请求耗时
filter False 启用搜索过滤
defaultModelsExpandDepth 1 模型展开深度
syntaxHighlight.theme "agate" 代码高亮主题

4. Tags 分组与 deprecated 标记

(1) Tags 分组端点

▶ 示例:定义 Tags

PYTHON
from fastapi import FastAPI, APIRouter

tags_metadata = [
    {
        "name": "auth",
        "description": "Authentication operations. Login, register, token refresh.",
    },
    {
        "name": "products",
        "description": "Product management. CRUD operations for tracked products.",
    },
    {
        "name": "prices",
        "description": "Price data. Query, import, and track price changes.",
    },
    {
        "name": "admin",
        "description": "Admin operations. Requires admin role.",
    },
]

app = FastAPI(
    title="PriceTracker API",
    openapi_tags=tags_metadata,
)

输出:

TEXT 📖 仅展示
# 执行成功

▶ 示例:端点分配 Tags

PYTHON
@app.post("/auth/login", tags=["auth"])
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    ...

@app.get("/api/v1/products", tags=["products"])
async def list_products():
    ...

@app.post("/api/v1/prices", tags=["prices"])
async def create_price():
    ...

@app.get("/api/v1/admin/stats", tags=["admin"])
async def admin_stats():
    ...

输出:

TEXT 📖 仅展示
# 函数定义成功

▶ 示例:deprecated 标记废弃端点

PYTHON
@app.get(
    "/api/v1/products/{product_id}/history",
    tags=["prices"],
    deprecated=True,
    summary="[DEPRECATED] Use GET /prices?product_id={id} instead",
)
async def get_price_history_deprecated(product_id: int):
    """This endpoint is deprecated. Use the prices endpoint with product_id filter."""
    ...

输出:

TEXT 📖 仅展示
# 函数定义成功

5. 示例值与响应模板

(1) json_schema_extra 与 examples

▶ 示例:Pydantic 模型示例值

PYTHON
from pydantic import BaseModel, Field

class ProductCreate(BaseModel):
    name: str = Field(
        min_length=1, max_length=200,
        description="Product display name",
        examples=["Wireless Mouse", "USB-C Hub", "Mechanical Keyboard"],
    )
    category: str = Field(
        max_length=100,
        description="Product category",
        examples=["electronics", "clothing", "food"],
    )
    base_price: float = Field(
        gt=0,
        description="Base price in USD",
        examples=[9.99, 49.99, 199.99],
    )

    model_config = {
        "json_schema_extra": {
            "examples": [
                {
                    "name": "Wireless Mouse",
                    "category": "electronics",
                    "base_price": 29.99,
                }
            ]
        }
    }

输出:

TEXT 📖 仅展示
# 执行成功

▶ 示例:端点级响应示例

PYTHON
from fastapi import FastAPI
from fastapi.responses import JSONResponse

@app.post(
    "/api/v1/products",
    response_model=ProductResponse,
    status_code=201,
    summary="Create a new product",
    description="Create a new product in the PriceTracker database.",
    responses={
        201: {
            "description": "Product created successfully",
            "content": {
                "application/json": {
                    "example": {
                        "id": 1,
                        "name": "Wireless Mouse",
                        "category": "electronics",
                        "base_price": 29.99,
                    }
                }
            },
        },
        401: {"description": "Authentication required"},
        403: {"description": "Insufficient permissions"},
        422: {"description": "Validation error"},
    },
)
async def create_product(product: ProductCreate):
    ...

输出:

TEXT 📖 仅展示
# 函数定义成功

6. 自定义 OpenAPI Schema

(1) generate_unique_id_function

▶ 示例:自定义端点 ID 生成

PYTHON
from fastapi import FastAPI

def custom_generate_unique_id(route):
    # Format: {method}_{tag}_{path}
    tags = route.tags or ["default"]
    tag = tags[0]
    method = route.methods.pop() if route.methods else "GET"
    path = route.path.replace("/", "_").strip("_").replace("{", "").replace("}", "")
    return f"{method}_{tag}_{path}"

app = FastAPI(
    generate_unique_id_function=custom_generate_unique_id,
)

输出:

TEXT 📖 仅展示
# 函数定义成功

(2) 文档最佳实践对比

维度 默认 最佳实践
端点分组 Tags 按功能分组
废弃端点 与正常端点混在一起 deprecated=True 灰色标记
请求示例 examples + json_schema_extra
响应模板 仅 200 201/401/403/422 全覆盖
描述 简单函数名 summary + description
认证说明 文档首页说明认证方式
版本化 单版本 URL 路径版本 /api/v1/

❓ 常见问题

Q Swagger UI 和 ReDoc 有什么区别?
A Swagger UI 交互式(可测试 API),ReDoc 阅读式(更美观)。对外文档推荐 ReDoc,内部开发用 Swagger UI。
Q 如何隐藏某些端点不在文档中显示?
A 设置 include_in_schema=False@app.get("/internal", include_in_schema=False)。适合内部健康检查、监控端点。
Q OpenAPI 版本用 3.0 还是 3.1?
A FastAPI 默认生成 OpenAPI 3.1(支持 JSON Schema 最新特性)。如需兼容旧工具,可在自定义 openapi 函数中降级。
Q 如何给文档加自定义 CSS/JS?
A 通过 swagger_ui_parameters 传 CSS URL,或用 get_swagger_ui_html 完全自定义 HTML。
Q 示例值和默认值有什么区别?
A examples 是文档展示用,不影响校验;default 是实际默认值,影响行为。两者都应该设置。
Q 多版本 API 的文档如何组织?
A 每个版本用独立的 APIRouter(prefix="/api/v1"),在 Tags 中区分版本。或为每个版本创建独立的 FastAPI 子应用。

📖 小节


📝 作业

  1. 基础题(难度⭐):为 PriceTracker 配置 FastAPI 应用级文档——标题、描述、版本号、联系方式,添加 persistAuthorization=True 让 Swagger UI 保留认证状态。提示:FastAPI(title=..., swagger_ui_parameters={...})
  2. 进阶题(难度⭐⭐):定义 4 个 Tags(auth/products/prices/admin),为每个端点分配正确的 Tag,标记废弃端点 deprecated=True,验证 Swagger UI 中端点按标签分组。提示:openapi_tags=[...] + tags=["products"]
  3. 挑战题(难度⭐⭐⭐):为 ProductCreate 添加 json_schema_extra 完整示例,为 POST /products 端点添加 201/401/403/422 多状态码响应模板含示例内容,实现自定义 generate_unique_id_function。提示:responses={201: {"content": {...}}} + generate_unique_id_function

---|

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏