第 14 章 · OpenAPI 定制与 SDK 生成
本章目标:深度定制 FastAPI 自动生成的 OpenAPI 3 文档;使用 tags 分组、summary/description 润色;为 Schema 与路由添加 examples 与 openapi_extra;配置 Swagger UI / ReDoc 元数据与安全方案 Bearer JWT;理解 openapi-generator(或 OpenAPI Generator)从契约生成 user-demo TypeScript 客户端的流程;让 svc-demo /docs 成为前后端单一真相来源。
学时建议:4~5 小时(含 2 小时文档站与生成客户端试跑)
前置:本模块 ch01~ch13;重点复习 ch04 响应模型、ch07 JWT、ch13 json_schema_extra 与 computed_field。
14.1 为什么需要 API 文档与契约
┌─────────────┐ OpenAPI 契约 ┌─────────────┐
│ user-demo │ ◄──────────────────► │ svc-demo │
│ 前端 SPA │ 路径/参数/响应模型 │ FastAPI API │
└─────────────┘ └─────────────┘
│ │
└──── openapi-generator ────────────┘
生成 TS SDK / 类型定义
| 无文档 | 有 OpenAPI 文档 |
|---|---|
| 口头同步字段,易漂移 | 单一契约,版本可追踪 |
| 联调靠猜状态码 | 错误码与示例一目了然 |
| 新人上手慢 | /docs 自助试调 |
| 前端 Mock 手写 | 从 schema 生成类型(本章) |
OpenAPI 3 核心:paths、components/schemas、responses、securitySchemes。
示例域名:api.example.com;前端:user-demo;禁止写入真实生产密钥。
14.2 FastAPI 应用级 OpenAPI 元数据
svc_demo/main.py:
from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi
app = FastAPI(
title="svc-demo API",
description=(
"教学项目 · 用户与商品微服务 REST API(虚构 api.example.com 契约)\n\n"
"## 认证\n"
"除公开端点外,请携带 `Authorization: Bearer <access_token>`。"
),
version="1.0.0",
contact={"name": "svc-demo 教学支持", "url": "https://docs.example.com/svc-demo"},
license_info={"name": "MIT"},
openapi_tags=[
{"name": "health", "description": "健康检查"},
{"name": "auth", "description": "注册、登录与当前用户"},
{"name": "products", "description": "商品资源 CRUD 与上下架"},
],
docs_url="/docs",
redoc_url="/redoc",
openapi_url="/openapi.json",
)
访问:
- Swagger UI:
http://127.0.0.1:8000/docs - ReDoc:
http://127.0.0.1:8000/redoc - 原始 JSON:
http://127.0.0.1:8000/openapi.json
14.3 tags 与路由组织
svc_demo/api/v1/router.py:
from fastapi import APIRouter
from .auth import router as auth_router
from .products import router as products_router
api_router = APIRouter(prefix="/api/v1")
api_router.include_router(auth_router)
api_router.include_router(products_router)
子路由指定 tag:
router = APIRouter(prefix="/auth", tags=["auth"])
| 实践 | 说明 |
|---|---|
| 按资源分 tag | products、auth,勿过细 |
openapi_tags 写描述 | Swagger 左侧分组可读 |
| 隐藏内部路由 | include_in_schema=False |
内部调试端点:
@router.get("/debug/cache-keys", include_in_schema=False)
async def debug_cache():
...
14.4 路由级 summary、description 与 response_description
@router.get(
"",
response_model=ListEnvelope[ProductOut],
summary="分页查询已上架商品",
description=(
"仅返回 `is_published=true` 的商品。支持名称模糊搜索参数 `q`。\n"
"列表默认走 Redis 缓存(ch11),TTL 300 秒。"
),
response_description="成功返回商品数组与分页元数据",
responses={
429: {
"description": "请求过于频繁",
"content": {
"application/json": {
"example": {"code": 42901, "message": "请求过于频繁,请稍后再试"},
}
},
},
},
)
async def list_products(...):
...
responses 字典会合并进 OpenAPI,补充非 response_model 覆盖的状态码(如 429)。
14.5 Schema examples:Field 与 model_config
Field examples(Pydantic v2)
from pydantic import BaseModel, Field
class LoginIn(BaseModel):
username: str = Field(..., examples=["admin"])
password: str = Field(..., examples=["AdminPass123!"])
json_schema_extra
class ProductCreate(BaseModel):
name: str
slug: str
price: Decimal
stock: int = 0
model_config = {
"json_schema_extra": {
"examples": [
{
"name": "FastAPI 实战",
"slug": "fastapi-book",
"price": "68.00",
"stock": 100,
}
]
}
}
Swagger UI「Example Value」将显示上述样本。
14.6 openapi_extra 与 Body 多示例
from fastapi import Body
@router.post(
"/products",
openapi_extra={
"requestBody": {
"content": {
"application/json": {
"examples": {
"book": {
"summary": "图书商品",
"value": {
"name": "Python 进阶",
"slug": "python-advanced",
"price": "88.00",
"stock": 50,
},
},
"draft": {
"summary": "未上架草稿",
"value": {
"name": "草稿商品",
"slug": "draft-item",
"price": "1.00",
"stock": 0,
"is_published": False,
},
},
}
}
}
}
},
)
async def create_product(
payload: ProductCreate = Body(...),
...
):
...
适用于一个端点多种典型请求体场景。
14.7 安全方案:Bearer JWT
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
bearer_scheme = HTTPBearer(