01-6. 自動生成 API 文件

⏱️ 閱讀時間: 10 分鐘 🎯 難度: ⭐ (入門)


🤔 一句話解釋

FastAPI 自動根據你的程式碼生成互動式 API 文件,不需要額外撰寫!


📚 兩種內建文件介面

FastAPI 內建兩種 API 文件介面:

Swagger UI

訪問: http://localhost:8000/docs

特色:

  • ✅ 互動式測試(直接在瀏覽器發送請求)
  • ✅ 清楚的請求/回應格式
  • ✅ 支援認證測試
  • ✅ 業界標準

ReDoc

訪問: http://localhost:8000/redoc

特色:

  • ✅ 更美觀的閱讀介面
  • ✅ 適合作為對外文件
  • ✅ 支援搜尋
  • ✅ 支援下載 OpenAPI 規格

🎨 自訂 API 資訊

基本設定

from fastapi import FastAPI

app = FastAPI(
    title="我的超棒 API",
    description="""
## 🚀 API 說明

這是一個功能強大的 API,提供以下功能:

* **使用者管理** - 註冊、登入、個人資料
* **商品管理** - 新增、編輯、刪除商品
* **訂單處理** - 建立訂單、追蹤狀態

### 認證方式

使用 JWT Bearer Token 進行認證。
    """,
    version="1.0.0",
    terms_of_service="https://example.com/terms/",
    contact={
        "name": "API Support",
        "url": "https://example.com/support",
        "email": "support@example.com",
    },
    license_info={
        "name": "MIT License",
        "url": "https://opensource.org/licenses/MIT",
    },
)

自訂文件路徑

app = FastAPI(
    docs_url="/api/docs",        # Swagger UI 路徑
    redoc_url="/api/redoc",      # ReDoc 路徑
    openapi_url="/api/openapi.json",  # OpenAPI schema 路徑
)

# 或者完全禁用(私有 API)
app = FastAPI(
    docs_url=None,
    redoc_url=None,
    openapi_url=None,
)

🏷️ 使用 Tags 組織 API

基本用法

from fastapi import FastAPI

app = FastAPI()

# 使用 tags 分類 API
@app.get("/users/", tags=["使用者"])
async def list_users():
    """列出所有使用者"""
    return []

@app.post("/users/", tags=["使用者"])
async def create_user():
    """建立新使用者"""
    return {}

@app.get("/items/", tags=["商品"])
async def list_items():
    """列出所有商品"""
    return []

@app.post("/items/", tags=["商品"])
async def create_item():
    """建立新商品"""
    return {}

定義 Tags 詳細資訊

from fastapi import FastAPI

tags_metadata = [
    {
        "name": "使用者",
        "description": "使用者相關操作,包含註冊、登入、個人資料管理",
    },
    {
        "name": "商品",
        "description": "商品相關操作",
        "externalDocs": {
            "description": "商品 API 詳細說明",
            "url": "https://example.com/docs/items",
        },
    },
    {
        "name": "訂單",
        "description": "訂單相關操作",
    },
]

app = FastAPI(
    title="電商 API",
    openapi_tags=tags_metadata
)

📝 撰寫好的 API 文件

Docstring 會自動出現在文件中

from fastapi import FastAPI, Path, Query
from pydantic import BaseModel, Field

app = FastAPI()

class UserCreate(BaseModel):
    """建立使用者的請求模型"""
    name: str = Field(..., description="使用者名稱", example="John Doe")
    email: str = Field(..., description="電子郵件", example="john@example.com")
    age: int = Field(..., ge=0, le=150, description="年齡", example=25)

class UserResponse(BaseModel):
    """使用者回應模型"""
    id: int = Field(..., description="使用者 ID", example=1)
    name: str = Field(..., description="使用者名稱", example="John Doe")
    email: str = Field(..., description="電子郵件", example="john@example.com")

@app.post(
    "/users/",
    response_model=UserResponse,
    tags=["使用者"],
    summary="建立新使用者",
    response_description="成功建立的使用者資訊"
)
async def create_user(user: UserCreate):
    """
    建立新使用者帳號

    - **name**: 使用者的顯示名稱,長度 1-100 字元
    - **email**: 唯一的電子郵件地址,會用於登入
    - **age**: 使用者年齡,必須介於 0-150 之間

    成功建立後會回傳使用者資訊(不包含密碼)。

    ### 注意事項

    1. Email 必須是唯一的
    2. 建立後會發送驗證郵件
    """
    return {"id": 1, "name": user.name, "email": user.email}

路徑參數和查詢參數的文件

from fastapi import FastAPI, Path, Query
from typing import Optional
from enum import Enum

app = FastAPI()

class SortOrder(str, Enum):
    asc = "asc"
    desc = "desc"

@app.get("/users/{user_id}", tags=["使用者"])
async def get_user(
    user_id: int = Path(
        ...,
        title="使用者 ID",
        description="要查詢的使用者 ID",
        ge=1,
        example=123
    ),
    include_posts: bool = Query(
        False,
        description="是否包含使用者的文章"
    ),
    include_comments: bool = Query(
        False,
        description="是否包含使用者的評論"
    )
):
    """
    根據 ID 獲取使用者資訊

    可選擇是否包含相關的文章和評論資料。
    """
    return {"user_id": user_id}

@app.get("/users/", tags=["使用者"])
async def list_users(
    skip: int = Query(0, ge=0, description="跳過的數量"),
    limit: int = Query(10, ge=1, le=100, description="返回的數量"),
    search: Optional[str] = Query(
        None,
        min_length=2,
        max_length=50,
        description="搜尋關鍵字(姓名或 email)"
    ),
    sort_by: str = Query("created_at", description="排序欄位"),
    order: SortOrder = Query(SortOrder.desc, description="排序方向")
):
    """
    列出使用者清單

    支援分頁、搜尋和排序功能。
    """
    return []

🎯 定義回應範例

方法 1:在 Field 中使用 example

from pydantic import BaseModel, Field

class Item(BaseModel):
    name: str = Field(..., example="iPhone 15")
    price: float = Field(..., example=999.99)
    description: str = Field(None, example="最新款智慧型手機")

方法 2:使用 model_config

from pydantic import BaseModel, ConfigDict

class Item(BaseModel):
    name: str
    price: float
    description: str = None

    model_config = ConfigDict(
        json_schema_extra={
            "example": {
                "name": "iPhone 15",
                "price": 999.99,
                "description": "最新款智慧型手機"
            }
        }
    )

方法 3:在路由中定義多個範例

from fastapi import FastAPI, Body

app = FastAPI()

@app.post("/items/")
async def create_item(
    item: Item = Body(
        ...,
        openapi_examples={
            "normal": {
                "summary": "一般商品",
                "description": "一般商品的範例",
                "value": {
                    "name": "筆記本",
                    "price": 99.9,
                    "description": "A5 大小"
                }
            },
            "expensive": {
                "summary": "高價商品",
                "description": "高價商品的範例",
                "value": {
                    "name": "MacBook Pro",
                    "price": 59900.0,
                    "description": "16 吋 M3 Max"
                }
            }
        }
    )
):
    return item

📊 定義多種回應

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    price: float

class Message(BaseModel):
    message: str

@app.get(
    "/items/{item_id}",
    response_model=Item,
    responses={
        200: {
            "description": "成功獲取商品",
            "content": {
                "application/json": {
                    "example": {"name": "iPhone", "price": 999.99}
                }
            }
        },
        404: {
            "description": "商品不存在",
            "model": Message,
            "content": {
                "application/json": {
                    "example": {"message": "商品不存在"}
                }
            }
        },
        422: {
            "description": "驗證錯誤"
        }
    }
)
async def get_item(item_id: int):
    if item_id == 0:
        raise HTTPException(status_code=404, detail="商品不存在")
    return {"name": "Item", "price": 10.0}

🔐 在文件中添加認證

from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials

app = FastAPI()

security = HTTPBearer()

@app.get("/protected", tags=["受保護的端點"])
async def protected_route(
    credentials: HTTPAuthorizationCredentials = Depends(security)
):
    """
    這是一個需要認證的端點

    需要在 Header 中提供 Bearer Token:
    ```
    Authorization: Bearer <your_token>
    ```
    """
    return {"token": credentials.credentials}

Swagger UI 會自動顯示「Authorize」按鈕!


📤 匯出 OpenAPI 規格

import json
from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi

app = FastAPI()

@app.get("/items/")
async def list_items():
    return []

# 獲取 OpenAPI schema
def custom_openapi():
    if app.openapi_schema:
        return app.openapi_schema

    openapi_schema = get_openapi(
        title="自訂 API",
        version="1.0.0",
        description="這是自訂的 API 描述",
        routes=app.routes,
    )

    # 可以在這裡自訂 schema
    openapi_schema["info"]["x-logo"] = {
        "url": "https://example.com/logo.png"
    }

    app.openapi_schema = openapi_schema
    return app.openapi_schema

app.openapi = custom_openapi

# 匯出為 JSON 檔案
if __name__ == "__main__":
    with open("openapi.json", "w") as f:
        json.dump(app.openapi(), f, indent=2)

✅ 重點總結

API 文件最佳實踐

# 1. 設定 API 基本資訊
app = FastAPI(
    title="我的 API",
    description="詳細說明...",
    version="1.0.0"
)

# 2. 使用 Tags 分類
@app.get("/users", tags=["使用者"])

# 3. 撰寫清楚的 docstring
async def create_user(user: UserCreate):
    """
    建立新使用者

    - **name**: 使用者名稱
    - **email**: 電子郵件
    """

# 4. 在 Pydantic 模型使用 Field
class User(BaseModel):
    name: str = Field(..., description="名稱", example="John")

# 5. 定義多種回應狀態
@app.get("/items/{id}", responses={404: {"model": ErrorResponse}})

文件網址

網址說明
/docsSwagger UI(互動式測試)
/redocReDoc(閱讀友善)
/openapi.jsonOpenAPI 規格(JSON)

🎤 面試這樣答

Q: FastAPI 如何自動生成 API 文件?

答案:

FastAPI 會自動根據程式碼中的 Type Hints、Pydantic 模型和 Docstring 生成 OpenAPI 規格(也叫 Swagger 規格)。

這包括:

  • 路由資訊(路徑、方法)
  • 參數定義(路徑參數、查詢參數、請求體)
  • 回應格式(從 response_model 推斷)
  • 驗證規則(從 Pydantic 的 Field 推斷)

然後內建的 Swagger UI 和 ReDoc 會渲染這個規格,提供互動式文件。


🤓 小測驗

  1. Swagger UI 的預設路徑是什麼?

  2. 如何在文件中將 API 分組?

  3. Pydantic 模型中如何添加欄位說明?


上一篇: 01-5. 狀態碼與錯誤處理 下一篇: 01-7. 專案結構最佳實踐


最後更新:2025-12-17

0%