Skill: cn-big-cabbage/fastapi-framework (version 0.1.0)

--- name: fastapi description: FastAPI - 高性能 Python Web API 框架 version: 0.1.0 metadata: openclaw: requires: - fastapi - uvicorn emoji: ⚡ homepage: https://fastapi.tiangolo.com --- # FastAPI 高性能 Python Web API 框架 ## 技能概述 本技能帮助开发者使用 FastAPI 构建现代化、高性能的 Web API，支持以下场景： - **REST API 开发**: 快速构建符合 OpenAPI 标准的 RESTful 接口 - **数据验证**: 基于 Pydantic 的自动请求/响应数据验证 - **自动文档**: 自动生成 Swagger UI 和 ReDoc 交互式 API 文档 - **异步支持**: 原生支持 async/await，处理高并发场景 - **依赖注入**: 强大的依赖注入系统，简化认证、数据库连接等公共逻辑 - **安全认证**: 内置 OAuth2、JWT、API Key 等认证方案 **技术基础**: FastAPI 构建于 Starlette（Web 框架层）和 Pydantic（数据验证层）之上，性能接近 NodeJS 和 Go。 ## 架构概览 ``` FastAPI 应用架构 ├── app/ │ ├── main.py # 应用入口，注册路由和中间件 │ ├── dependencies.py # 公共依赖（认证、数据库会话等） │ ├── models/ # Pydantic 数据模型 │ │ ├── __init__.py │ │ └── user.py │ ├── routers/ # 路由模块（按业务划分） │ │ ├── __init__.py │ │ ├── users.py │ │ └── items.py │ ├── crud/ # 数据库操作层 │ │ ├── __init__.py │ │ └── user.py │ └── core/ # 核心配置 │ ├── config.py │ └── security.py ``` ## 核心概念 | 概念 | 说明 | |------|------| | **路径操作** | 使用 `@app.get/post/put/delete` 装饰器定义 API 端点 | | **路径参数** | URL 中的变量，如 `/items/{item_id}` | | **查询参数** | URL 查询字符串，如 `/items?skip=0&limit=10` | | **请求体** | 通过 Pydantic 模型接收 JSON 数据 | | **依赖注入** | 通过 `Depends()` 注入可复用的依赖函数 | | **中间件** | 处理每个请求/响应的通用逻辑 | | **路由器** | `APIRouter` 将路由按模块组织，类似 Flask Blueprint | ## 使用流程 AI 助手将引导你完成以下步骤： 1. 安装 FastAPI 及依赖（uvicorn、pydantic 等） 2. 创建应用入口文件 `main.py` 3. 定义 Pydantic 数据模型 4. 编写路径操作函数（路由处理器） 5. 配置依赖注入和中间件 6. 启动开发服务器并验证 API 文档 ## 关键章节导航 - [安装指南](./guides/01-installation.md) - [快速开始](./guides/02-quickstart.md) - [高级用法](./guides/03-advanced-usage.md) - [常见问题](./troubleshooting.md) ## AI 助手能力 当你向 AI 描述需求时，AI 会： - **自动生成** 路由处理函数和 Pydantic 数据模型 - **自动配置** 依赖注入（数据库会话、用户认证） - **自动搭建** 项目骨架结构（models、routers、crud 分层） - **自动实现** JWT 认证和 OAuth2 安全方案 - **自动集成** SQLAlchemy/SQLModel 数据库操作 - **自动编写** 测试用例（pytest + httpx） - **自动处理** CORS 配置和自定义中间件 ## 核心功能 - ✅ 基于 Python 类型提示的自动数据验证 - ✅ 自动生成 OpenAPI/Swagger 交互式文档 - ✅ 原生 async/await 异步支持 - ✅ 强大的依赖注入系统 - ✅ 内置安全认证（OAuth2、JWT、API Key、HTTP Basic） - ✅ 支持 WebSocket 实时通信 - ✅ 支持 GraphQL（通过 Strawberry 集成） - ✅ 支持后台任务（BackgroundTasks） - ✅ 支持文件上传和静态文件服务 - ✅ 支持中间件和 CORS 配置 ## 快速示例 ```python from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class Item(BaseModel): name: str price: float is_offer: bool | None = None @app.get("/") async def read_root(): return {"Hello": "World"} @app.get("/items/{item_id}") async def read_item(item_id: int, q: str | None = None): return {"item_id": item_id, "q": q} @app.put("/items/{item_id}") async def update_item(item_id: int, item: Item): return {"item_name": item.name, "item_id": item_id} ``` ```bash # 启动开发服务器 fastapi dev main.py # 或使用 uvicorn uvicorn main:app --reload ``` 访问 `http://127.0.0.1:8000/docs` 查看自动生成的交互式 API 文档。 ## 扩展点 - **中间件**: 实现日志、限流、请求追踪等横切关注点 - **依赖注入**: 封装数据库连接、权限校验等可复用逻辑 - **事件处理**: `startup`/`shutdown` 生命周期钩子 - **自定义响应**: 支持 `JSONResponse`、`HTMLResponse`、`FileResponse` 等 - **插件生态**: 支持 SQLAlchemy、Celery、Redis、S3 等主流库 ## 环境要求 - Python 3.8 或更高版本（推荐 3.11+） - pip 包管理器 - （可选）虚拟环境工具（venv、conda、uv） ## 许可证 MIT License ## 项目链接 - GitHub: https://github.com/tiangolo/fastapi - 官网: https://fastapi.tiangolo.com - 文档: https://fastapi.tiangolo.com/tutorial/ - PyPI: https://pypi.org/project/fastapi/

path: guides/01-installation.md
content: # 安装指南

**适用场景**: 首次使用 FastAPI，需要安装和配置开发环境

---

## 一、安装前准备

### 目标

确保系统具备运行 FastAPI 的基础 Python 环境

### 前置条件

- Python 3.8 或更高版本（推荐 3.11+）
- pip 包管理器
- （推荐）虚拟环境工具

### 检查 Python 环境

**AI 执行说明**: AI 将自动检查你的 Python 版本和 pip 可用性

```bash
# 检查 Python 版本
python --version
# 或
python3 --version

# 检查 pip
pip --version

# 检查 Python 路径
which python
```

**期望结果**:

- Python 3.8+ ✅
- pip 已安装 ✅

---

## 二、创建虚拟环境（推荐）

使用虚拟环境可以隔离项目依赖，避免版本冲突。

### 方法 1：使用 venv（Python 内置）

**AI 执行说明**: AI 可以自动创建并激活虚拟环境

```bash
# 创建虚拟环境（在项目目录中）
python -m venv .venv

# 激活虚拟环境
# macOS / Linux
source .venv/bin/activate

# Windows (PowerShell)
.venv\Scripts\Activate.ps1

# Windows (CMD)
.venv\Scripts\activate.bat
```

**验证激活状态**:

```bash
# 激活后命令行前会显示 (.venv)
which python  # 应指向 .venv/bin/python
```

### 方法 2：使用 uv（更快的现代工具）

```bash
# 安装 uv
pip install uv

# 创建并激活虚拟环境
uv venv
source .venv/bin/activate

# 使用 uv 安装包（比 pip 快 10-100x）
uv pip install "fastapi[standard]"
```

### 方法 3：使用 conda

```bash
# 创建 conda 环境
conda create -n myproject python=3.11

# 激活环境
conda activate myproject
```

---

## 三、安装 FastAPI

### 标准安装（推荐）

**AI 执行说明**: AI 可以直接执行安装命令

```bash
pip install "fastapi[standard]"
```

`[standard]` 安装包含以下依赖：

| 包名 | 用途 |
|------|------|
| `uvicorn[standard]` | ASGI 服务器，运行 FastAPI 应用 |
| `pydantic` | 数据验证和序列化 |
| `fastapi-cli` | `fastapi dev` / `fastapi run` 命令行工具 |
| `email-validator` | Email 字段验证 |
| `httpx` | 测试客户端 |
| `jinja2` | 模板引擎（可选） |
| `python-multipart` | 表单和文件上传支持 |

### 最小化安装

如果只需要 FastAPI 核心功能：

```bash
# 仅安装 FastAPI
pip install fastapi

# 单独安装 ASGI 服务器
pip install "uvicorn[standard]"
```

### 验证安装

**AI 执行说明**: AI 将验证 FastAPI 是否正确安装

```bash
# 检查 FastAPI 版本
python -c "import fastapi; print(fastapi.__version__)"

# 检查 uvicorn 版本
uvicorn --version

# 检查 pydantic 版本
python -c "import pydantic; print(pydantic.__version__)"
```

**期望输出示例**:

```
0.115.0
Running uvicorn 0.32.0 with CPython 3.11.0 on Darwin
2.9.2
```

---

## 四、安装可选依赖

根据项目需求选择安装：

### 数据库支持

```bash
# SQLModel（推荐，基于 SQLAlchemy + Pydantic）
pip install sqlmodel

# 纯 SQLAlchemy
pip install sqlalchemy

# PostgreSQL 驱动
pip install asyncpg          # 异步驱动（推荐生产环境）
pip install psycopg2-binary  # 同步驱动

# MySQL 驱动
pip install aiomysql

# SQLite（Python 内置，无需安装）
```

### 安全认证

```bash
# JWT 令牌
pip install pyjwt

# 密码哈希
pip install "pwdlib[argon2]"

# 或使用 passlib（旧版方案）
pip install "passlib[bcrypt]"
```

### 测试工具

```bash
# pytest 测试框架
pip install pytest

# 异步测试支持
pip install anyio pytest-anyio

# HTTP 测试客户端（已包含在 standard 中）
pip install httpx
```

### 其他常用包

```bash
# 环境变量管理
pip install python-dotenv

# 跨域支持（FastAPI 内置，无需额外安装）
# 使用 app.add_middleware(CORSMiddleware, ...) 即可

# 后台任务队列
pip install celery redis

# 缓存
pip install aioredis
```

---

## 五、生成 requirements.txt

```bash
# 导出当前环境依赖
pip freeze > requirements.txt

# 从 requirements.txt 安装
pip install -r requirements.txt
```

**典型的 requirements.txt 示例**:

```
fastapi[standard]>=0.115.0
sqlmodel>=0.0.21
pyjwt>=2.9.0
pwdlib[argon2]>=0.2.0
python-dotenv>=1.0.0
```

---

## 六、使用 pyproject.toml（现代项目推荐）

```toml
[project]
name = "my-fastapi-app"
version = "0.1.0"
requires-python = ">=3.8"
dependencies = [
    "fastapi[standard]>=0.115.0",
    "sqlmodel>=0.0.21",
    "pyjwt>=2.9.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=8.0.0",
    "httpx>=0.27.0",
]
```

---

## 七、常见安装问题

### 问题 1：Python 版本过低

**错误**: `ERROR: Package 'fastapi' requires a different Python`

**解决方案**:

```bash
# 检查当前版本
python --version

# 使用 pyenv 安装新版本 Python
pyenv install 3.11.0
pyenv local 3.11.0
```

### 问题 2：pip 网络超时

**解决方案**:

```bash
# 使用清华大学镜像源
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple "fastapi[standard]"

# 或配置永久镜像
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
```

### 问题 3：权限错误

**解决方案**:

```bash
# 使用用户级别安装（不推荐，优先使用虚拟环境）
pip install --user "fastapi[standard]"
```

### 问题 4：pydantic v1/v2 兼容性问题

FastAPI 0.100+ 使用 Pydantic v2，API 有所变化。

```bash
# 查看当前 pydantic 版本
python -c "import pydantic; print(pydantic.VERSION)"

# 如需强制使用 pydantic v1 兼容模式
pip install "pydantic[v1]"
```

---

## 完成确认

### 检查清单

- [ ] Python 3.8+ 已安装
- [ ] 虚拟环境已创建并激活
- [ ] FastAPI 已安装（`fastapi[standard]`）
- [ ] uvicorn 可用（`uvicorn --version`）
- [ ] 导入测试通过（`python -c "import fastapi"`）

### 下一步

继续阅读 [快速开始](02-quickstart.md) 创建你的第一个 FastAPI 应用


path: guides/02-quickstart.md
content: # 快速开始

**适用场景**: 已完成安装，准备创建第一个 FastAPI 应用

---

## 一、Hello World 示例

### 创建第一个应用

**AI 执行说明**: AI 可以自动生成并运行以下代码

创建文件 `main.py`：

```python
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def read_root():
    return {"Hello": "World"}

@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
    return {"item_id": item_id, "q": q}
```

### 启动开发服务器

```bash
# 方式 1：使用 fastapi CLI（推荐）
fastapi dev main.py

# 方式 2：使用 uvicorn
uvicorn main:app --reload

# 方式 3：指定主机和端口
uvicorn main:app --host 0.0.0.0 --port 8080 --reload
```

**期望输出**:

```
INFO:     Will watch for changes in these directories: ['/path/to/project']
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [28720]
INFO:     Started server process [28722]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
```

### 验证 API

```bash
# 测试根路径
curl http://127.0.0.1:8000/
# 返回: {"Hello":"World"}

# 测试带参数的路径
curl "http://127.0.0.1:8000/items/5?q=somequery"
# 返回: {"item_id":5,"q":"somequery"}
```

访问交互式文档：

- **Swagger UI**: http://127.0.0.1:8000/docs
- **ReDoc**: http://127.0.0.1:8000/redoc
- **OpenAPI JSON**: http://127.0.0.1:8000/openapi.json

---

## 二、路径参数和查询参数

### 路径参数（Path Parameters）

路径参数通过 URL 路径传递，使用 `{变量名}` 语法：

```python
from fastapi import FastAPI

app = FastAPI()

@app.get("/users/{user_id}")
async def read_user(user_id: int):
    return {"user_id": user_id}

# 枚举路径参数
from enum import Enum

class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"

@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    if model_name is ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}
    return {"model_name": model_name, "message": "Have some residuals"}
```

### 查询参数（Query Parameters）

不在路径中的参数自动变为查询参数：

```python
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]

@app.get("/items/")
async def read_items(skip: int = 0, limit: int = 10):
    return fake_items_db[skip : skip + limit]

# 可选查询参数
@app.get("/items/{item_id}")
async def read_item(item_id: str, q: str | None = None, short: bool = False):
    item = {"item_id": item_id}
    if q:
        item.update({"q": q})
    if not short:
        item.update({"description": "This has a long description"})
    return item
```

---

## 三、请求体（Request Body）

使用 Pydantic 模型定义请求体数据结构：

```python
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

@app.post("/items/")
async def create_item(item: Item):
    item_dict = item.model_dump()
    if item.tax is not None:
        price_with_tax = item.price + item.tax
        item_dict.update({"price_with_tax": price_with_tax})
    return item_dict

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    return {"item_id": item_id, **item.model_dump()}
```

**发送 POST 请求**:

```bash
curl -X POST "http://127.0.0.1:8000/items/" \
  -H "Content-Type: application/json" \
  -d '{"name": "Foo", "price": 45.2}'
```

---

## 四、数据验证

### 字段验证

使用 `Query`、`Path`、`Body` 添加验证规则：

```python
from typing import Annotated
from fastapi import FastAPI, Query, Path

app = FastAPI()

@app.get("/items/")
async def read_items(
    q: Annotated[str | None, Query(min_length=3, max_length=50, pattern="^fixedquery$")] = None
):
    results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        results.update({"q": q})
    return results

@app.get("/items/{item_id}")
async def read_item(
    item_id: Annotated[int, Path(title="The ID of the item", ge=1)],
    q: Annotated[str | None, Query(alias="item-query")] = None,
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    return results
```

### Pydantic 模型验证

```python
from pydantic import BaseModel, Field, HttpUrl

class Image(BaseModel):
    url: HttpUrl
    name: str

class Item(BaseModel):
    name: str
    description: str | None = Field(default=None, max_length=300)
    price: float = Field(gt=0, description="Price must be greater than zero")
    tags: list[str] = []
    image: Image | None = None
```

---

## 五、响应模型

使用 `response_model` 控制返回数据结构，过滤敏感字段：

```python
from pydantic import BaseModel, EmailStr

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("/users/", response_model=UserOut)
async def create_user(user: UserIn):
    # password 字段不会出现在响应中
    return user
```

**响应状态码**:

```python
from fastapi import status

@app.post("/items/", status_code=status.HTTP_201_CREATED)
async def create_item(name: str):
    return {"name": name}
```

---

## 六、错误处理

### 使用 HTTPException

```python
from fastapi import FastAPI, HTTPException

app = FastAPI()

fake_items_db = {"foo": "Foo", "bar": "Bar"}

@app.get("/items/{item_id}")
async def read_item(item_id: str):
    if item_id not in fake_items_db:
        raise HTTPException(
            status_code=404,
            detail=f"Item '{item_id}' not found",
        )
    return {"item": fake_items_db[item_id]}
```

### 自定义异常处理器

```python
from fastapi import Request
from fastapi.responses import JSONResponse

class UnicornException(Exception):
    def __init__(self, name: str):
        self.name = name

@app.exception_handler(UnicornException)
async def unicorn_exception_handler(request: Request, exc: UnicornException):
    return JSONResponse(
        status_code=418,
        content={"message": f"Oops! {exc.name} did something wrong."},
    )

@app.get("/unicorns/{name}")
async def read_unicorn(name: str):
    if name == "yolo":
        raise UnicornException(name=name)
    return {"unicorn_name": name}
```

---

## 七、表单和文件上传

### 接收表单数据

```bash
pip install python-multipart
```

```python
from typing import Annotated
from fastapi import FastAPI, Form

app = FastAPI()

@app.post("/login/")
async def login(
    username: Annotated[str, Form()],
    [REDACTED_SECRET], Form()]
):
    return {"username": username}
```

### 文件上传

```python
from fastapi import File, UploadFile

@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
    contents = await file.read()
    return {
        "filename": file.filename,
        "content_type": file.content_type,
        "size": len(contents),
    }

# 多文件上传
@app.post("/uploadfiles/")
async def create_upload_files(files: list[UploadFile]):
    return {"filenames": [file.filename for file in files]}
```

---

## 八、完整 CRUD 示例

以下是一个完整的商品管理 API 示例：

```python
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI(title="商品管理 API", version="1.0.0")

# 数据模型
class ItemBase(BaseModel):
    name: str
    description: str | None = None
    price: float

class ItemCreate(ItemBase):
    pass

class Item(ItemBase):
    id: int

    class Config:
        from_attributes = True

# 模拟数据库
fake_db: dict[int, Item] = {}
next_id = 1

@app.get("/items/", response_model=list[Item])
async def list_items(skip: int = 0, limit: int = 10):
    items = list(fake_db.values())
    return items[skip : skip + limit]

@app.post("/items/", response_model=Item, status_code=201)
async def create_item(item: ItemCreate):
    global next_id
    db_item = Item(id=next_id, **item.model_dump())
    fake_db[next_id] = db_item
    next_id += 1
    return db_item

@app.get("/items/{item_id}", response_model=Item)
async def get_item(item_id: int):
    if item_id not in fake_db:
        raise HTTPException(status_code=404, detail="Item not found")
    return fake_db[item_id]

@app.put("/items/{item_id}", response_model=Item)
async def update_item(item_id: int, item: ItemCreate):
    if item_id not in fake_db:
        raise HTTPException(status_code=404, detail="Item not found")
    updated = Item(id=item_id, **item.model_dump())
    fake_db[item_id] = updated
    return updated

@app.delete("/items/{item_id}", status_code=204)
async def delete_item(item_id: int):
    if item_id not in fake_db:
        raise HTTPException(status_code=404, detail="Item not found")
    del fake_db[item_id]
```

---

## 九、编写测试

**AI 执行说明**: AI 可以自动生成测试代码并执行测试

```python
# test_main.py
from fastapi.testclient import TestClient
from main import app

client = TestClient(app)

def test_read_root():
    response = client.get("/")
    assert response.status_code == 200
    assert response.json() == {"Hello": "World"}

def test_read_item():
    response = client.get("/items/5")
    assert response.status_code == 200
    assert response.json() == {"item_id": 5, "q": None}

def test_read_item_with_query():
    response = client.get("/items/5?q=test")
    assert response.status_code == 200
    assert response.json()["q"] == "test"
```

```bash
# 运行测试
pytest test_main.py -v
```

---

## 完成确认

### 检查清单

- [ ] 成功启动开发服务器
- [ ] 浏览器访问 `/docs` 可见 Swagger UI
- [ ] GET/POST/PUT/DELETE 端点正常工作
- [ ] 数据验证错误返回正确的 422 状态码
- [ ] 测试用例通过

### 下一步

继续阅读 [高级用法](03-advanced-usage.md) 了解路由器、依赖注入、认证等进阶功能


path: guides/03-advanced-usage.md
content: # 高级用法

**适用场景**: 构建生产级 FastAPI 应用，涉及模块化路由、依赖注入、认证、数据库集成、中间件等

---

## 一、模块化项目结构（APIRouter）

### 推荐项目结构

```
app/
├── __init__.py
├── main.py              # 应用入口
├── dependencies.py      # 公共依赖
├── core/
│   ├── __init__.py
│   ├── config.py        # 配置管理
│   └── security.py      # 安全工具函数
├── models/
│   ├── __init__.py
│   └── user.py          # Pydantic 模型
├── routers/
│   ├── __init__.py
│   ├── users.py
│   └── items.py
└── crud/
    ├── __init__.py
    └── user.py
```

### 定义路由器

**AI 执行说明**: AI 可以自动生成路由器模块代码

```python
# app/routers/users.py
from fastapi import APIRouter, Depends, HTTPException
from ..dependencies import get_current_user

router = APIRouter(
    prefix="/users",
    tags=["users"],
    responses={404: {"description": "Not found"}},
)

@router.get("/", summary="获取用户列表")
async def read_users():
    return [{"username": "Rick"}, {"username": "Morty"}]

@router.get("/me", summary="获取当前用户")
async def read_users_me(current_user: dict = Depends(get_current_user)):
    return current_user

@router.get("/{username}", summary="根据用户名获取用户")
async def read_user(username: str):
    return {"username": username}
```

```python
# app/routers/items.py
from fastapi import APIRouter, Depends, HTTPException
from ..dependencies import get_token_header

router = APIRouter(
    prefix="/items",
    tags=["items"],
    dependencies=[Depends(get_token_header)],  # 路由器级别依赖
    responses={404: {"description": "Not found"}},
)

fake_items_db = {"plumbus": {"name": "Plumbus"}, "gun": {"name": "Portal Gun"}}

@router.get("/")
async def read_items():
    return fake_items_db

@router.get("/{item_id}")
async def read_item(item_id: str):
    if item_id not in fake_items_db:
        raise HTTPException(status_code=404, detail="Item not found")
    return {"name": fake_items_db[item_id]["name"], "item_id": item_id}
```

### 主应用注册路由器

```python
# app/main.py
from fastapi import FastAPI
from .routers import users, items

app = FastAPI(
    title="My API",
    description="Production FastAPI Application",
    version="1.0.0",
)

# 注册路由器
app.include_router(users.router)
app.include_router(items.router)

# 还可以在注册时覆盖前缀和 tags
app.include_router(
    users.router,
    prefix="/api/v2",
    tags=["users-v2"],
)

@app.get("/", tags=["root"])
async def root():
    return {"message": "Welcome to My API"}
```

---

## 二、依赖注入系统

### 基础依赖

**AI 执行说明**: AI 可以自动识别公共逻辑并封装为依赖函数

```python
# app/dependencies.py
from typing import Annotated
from fastapi import Depends, HTTPException, Header

# 公共查询参数
async def common_parameters(
    q: str | None = None,
    skip: int = 0,
    limit: int = 100,
):
    return {"q": q, "skip": skip, "limit": limit}

CommonsDep = Annotated[dict, Depends(common_parameters)]

# Token 验证依赖
async def get_token_header(x_token: Annotated[str, Header()]):
    if x_token != "fake-super-secret-token":
        raise HTTPException(status_code=400, detail="X-Token header invalid")

# 使用类作为依赖（可缓存）
class DatabaseDep:
    def __init__(self, db_url: str = "sqlite:///./test.db"):
        self.db_url = db_url

    def __call__(self):
        # 返回数据库连接
        return {"url": self.db_url}

db_dependency = DatabaseDep()
```

### 带 yield 的依赖（资源管理）

适用于数据库会话、文件句柄等需要在请求结束后释放的资源：

```python
from sqlmodel import Session, create_engine

engine = create_engine("sqlite:///./database.db")

def get_session():
    with Session(engine) as session:
        yield session  # FastAPI 在请求结束后自动关闭 session

SessionDep = Annotated[Session, Depends(get_session)]
```

### 子依赖（依赖链）

```python
from fastapi.security import OAuth2PasswordBearer

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

async def get_current_user([REDACTED_SECRET], Depends(oauth2_scheme)]):
    user = decode_token(token)
    if not user:
        raise HTTPException(
            status_code=401,
            detail="Invalid authentication credentials",
            headers={"WWW-Authenticate": "Bearer"},
        )
    return user

async def get_current_active_user(
    current_user: Annotated[dict, Depends(get_current_user)]
):
    if current_user.get("disabled"):
        raise HTTPException(status_code=400, detail="Inactive user")
    return current_user

# 在路由中使用
@app.get("/users/me")
async def read_users_me(
    current_user: Annotated[dict, Depends(get_current_active_user)]
):
    return current_user
```

---

## 三、中间件

### 自定义 HTTP 中间件

```python
import time
from fastapi import FastAPI, Request

app = FastAPI()

@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    start_time = time.perf_counter()
    response = await call_next(request)
    process_time = time.perf_counter() - start_time
    response.headers["X-Process-Time"] = str(process_time)
    return response

# 日志中间件
@app.middleware("http")
async def log_requests(request: Request, call_next):
    print(f"Request: {request.method} {request.url}")
    response = await call_next(request)
    print(f"Response status: {response.status_code}")
    return response
```

### CORS 配置

```python
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://example.com", "https://www.example.com"],
    allow_credentials=True,
    allow_methods=["GET", "POST", "PUT", "DELETE"],
    allow_headers=["*"],
    expose_headers=["X-Process-Time"],
)

# 开发环境允许所有来源
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)
```

### GZip 压缩中间件

```python
from fastapi.middleware.gzip import GZipMiddleware

app.add_middleware(GZipMiddleware, minimum_size=1000)
```

---

## 四、JWT 认证实现

### 安装依赖

```bash
pip install pyjwt "pwdlib[argon2]"
```

### 完整认证实现

```python
# app/core/security.py
from datetime import datetime, timedelta, timezone
from typing import Annotated
import jwt
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pwdlib import PasswordHash
from pydantic import BaseModel

SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

password_hash = PasswordHash.recommended()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

# 数据模型
class Token(BaseModel):
    access_token: str
    token_type: str

class TokenData(BaseModel):
    username: str | None = None

class User(BaseModel):
    username: str
    email: str | None = None
    disabled: bool | None = None

class UserInDB(User):
    hashed_password: str

# 模拟用户数据库
fake_users_db = {
    "johndoe": {
        "username": "johndoe",
        "email": "[REDACTED_SECRET]",
        "hashed_password": password_hash.hash("secret"),
        "disabled": False,
    }
}

def verify_password(plain_password: str, hashed_password: str) -> bool:
    return password_hash.verify(plain_password, hashed_password)

def get_password_hash(password: str) -> str:
    return password_hash.hash(password)

def get_user(db, username: str) -> UserInDB | None:
    if username in db:
        return UserInDB(**db[username])
    return None

def authenticate_user(db, username: str, password: str) -> UserInDB | bool:
    user = get_user(db, username)
    if not user:
        return False
    if not verify_password(password, user.hashed_password):
        return False
    return user

def create_access_token(data: dict, expires_delta: timedelta | None = None) -> str:
    to_encode = {**data}
    expire = datetime.now(timezone.utc) + (expires_delta or timedelta(minutes=15))
    to_encode.update({"exp": expire})
    return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)

async def get_current_user([REDACTED_SECRET], Depends(oauth2_scheme)]) -> User:
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
        token_data = TokenData(username=username)
    except jwt.InvalidTokenError:
        raise credentials_exception
    user = get_user(fake_users_db, username=token_data.username)
    if user is None:
        raise credentials_exception
    return user

async def get_current_active_user(
    current_user: Annotated[User, Depends(get_current_user)]
) -> User:
    if current_user.disabled:
        raise HTTPException(status_code=400, detail="Inactive user")
    return current_user
```

```python
# app/main.py（认证相关路由）
from fastapi import FastAPI, Depends
from .core.security import (
    Token, User, authenticate_user, create_access_token,
    get_current_active_user, fake_users_db, ACCESS_TOKEN_EXPIRE_MINUTES
)
from fastapi.security import OAuth2PasswordRequestForm
from datetime import timedelta
from typing import Annotated

app = FastAPI()

@app.post("/token", response_model=Token)
async def login_for_access_token(
    form_data: Annotated[OAuth2PasswordRequestForm, Depends()]
) -> Token:
    user = authenticate_user(fake_users_db, form_data.username, form_data.password)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect username or password",
            headers={"WWW-Authenticate": "Bearer"},
        )
    access_token = create_access_token(
        data={"sub": user.username},
        expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES),
    )
    return Token(access_token=access_token, token_type="bearer")

@app.get("/users/me/", response_model=User)
async def read_users_me(
    current_user: Annotated[User, Depends(get_current_active_user)]
):
    return current_user
```

---

## 五、数据库集成（SQLModel）

### 安装和配置

```bash
pip install sqlmodel
```

```python
# app/database.py
from typing import Annotated
from fastapi import Depends
from sqlmodel import Field, Session, SQLModel, create_engine

DATABASE_URL = "sqlite:///./database.db"
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})

def create_db_and_tables():
    SQLModel.metadata.create_all(engine)

def get_session():
    with Session(engine) as session:
        yield session

SessionDep = Annotated[Session, Depends(get_session)]
```

### 模型定义（继承模式）

```python
# app/models/hero.py
from sqlmodel import Field, SQLModel

# 共享基础字段
class HeroBase(SQLModel):
    name: str = Field(index=True)
    age: int | None = Field(default=None, index=True)

# 数据库表模型
class Hero(HeroBase, table=True):
    id: int | None = Field(default=None, primary_key=True)
    secret_name: str  # 敏感字段，不对外暴露

# 创建请求模型
class HeroCreate(HeroBase):
    secret_name: str

# 公开响应模型（不含敏感字段）
class HeroPublic(HeroBase):
    id: int

# 更新请求模型（所有字段可选）
class HeroUpdate(SQLModel):
    name: str | None = None
    age: int | None = None
    secret_name: str | None = None
```

### CRUD 路由实现

```python
# app/routers/heroes.py
from fastapi import APIRouter, HTTPException, Query
from sqlmodel import select
from ..database import SessionDep
from ..models.hero import Hero, HeroCreate, HeroPublic, HeroUpdate

router = APIRouter(prefix="/heroes", tags=["heroes"])

@router.post("/", response_model=HeroPublic)
def create_hero(hero: HeroCreate, session: SessionDep):
    db_hero = Hero.model_validate(hero)
    session.add(db_hero)
    session.commit()
    session.refresh(db_hero)
    return db_hero

@router.get("/", response_model=list[HeroPublic])
def read_heroes(
    session: SessionDep,
    offset: int = 0,
    limit: int = Query(default=100, le=100),
):
    heroes = session.exec(select(Hero).offset(offset).limit(limit)).all()
    return heroes

@router.get("/{hero_id}", response_model=HeroPublic)
def read_hero(hero_id: int, session: SessionDep):
    hero = session.get(Hero, hero_id)
    if not hero:
        raise HTTPException(status_code=404, detail="Hero not found")
    return hero

@router.patch("/{hero_id}", response_model=HeroPublic)
def update_hero(hero_id: int, hero: HeroUpdate, session: SessionDep):
    hero_db = session.get(Hero, hero_id)
    if not hero_db:
        raise HTTPException(status_code=404, detail="Hero not found")
    hero_data = hero.model_dump(exclude_unset=True)
    hero_db.sqlmodel_update(hero_data)
    session.add(hero_db)
    session.commit()
    session.refresh(hero_db)
    return hero_db

@router.delete("/{hero_id}")
def delete_hero(hero_id: int, session: SessionDep):
    hero = session.get(Hero, hero_id)
    if not hero:
        raise HTTPException(status_code=404, detail="Hero not found")
    session.delete(hero)
    session.commit()
    return {"ok": True}
```

---

## 六、应用生命周期事件

使用 `lifespan` 管理应用启动和关闭资源：

```python
from contextlib import asynccontextmanager
from fastapi import FastAPI

@asynccontextmanager
async def lifespan(app: FastAPI):
    # 启动时：初始化数据库、连接缓存等
    print("Application starting up...")
    create_db_and_tables()
    # 也可以初始化 ML 模型
    # app.state.model = load_model()

    yield  # 应用运行中

    # 关闭时：释放资源
    print("Application shutting down...")
    # await cache.close()

app = FastAPI(lifespan=lifespan)
```

---

## 七、后台任务

```python
from fastapi import BackgroundTasks

def write_log(message: str):
    with open("log.txt", mode="a") as log:
        log.write(message + "\n")

async def send_email(email: str, message: str):
    # 模拟发送邮件
    print(f"Sending email to {email}: {message}")

@app.post("/send-notification/{email}")
async def send_notification(
    email: str,
    background_tasks: BackgroundTasks,
):
    background_tasks.add_task(write_log, f"Notification sent to {email}")
    background_tasks.add_task(send_email, email, "You have a notification!")
    return {"message": "Notification sent in the background"}
```

---

## 八、WebSocket 支持

```python
from fastapi import FastAPI, WebSocket
from fastapi.responses import HTMLResponse

app = FastAPI()

@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    try:
        while True:
            data = await websocket.receive_text()
            await websocket.send_text(f"Message received: {data}")
    except Exception:
        await websocket.close()
```

---

## 九、环境配置管理

```python
# app/core/config.py
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    app_name: str = "My FastAPI App"
    debug: bool = False
    database_url: str = "sqlite:///./database.db"
    secret_key: str
    access_token_expire_minutes: int = 30

    class Config:
        env_file = ".env"

settings = Settings()
```

```bash
# .env
APP_NAME=Production API
DEBUG=false
DATABASE_URL=postgresql://user:pass@localhost/mydb
SECRET_KEY=super-secret-key
```

```bash
pip install pydantic-settings
```

---

## 十、API 版本控制

```python
# 方式 1：URL 前缀版本
from fastapi import FastAPI
from .routers.v1 import users as users_v1
from .routers.v2 import users as users_v2

app = FastAPI()
app.include_router(users_v1.router, prefix="/api/v1")
app.include_router(users_v2.router, prefix="/api/v2")

# 方式 2：独立子应用挂载
from fastapi import FastAPI

v1 = FastAPI()
v2 = FastAPI()

@v1.get("/users/")
async def read_users_v1():
    return {"version": "v1"}

@v2.get("/users/")
async def read_users_v2():
    return {"version": "v2", "features": ["new_feature"]}

app = FastAPI()
app.mount("/api/v1", v1)
app.mount("/api/v2", v2)
```

---

## 十一、生产部署

### 使用 Gunicorn + Uvicorn

```bash
pip install gunicorn

# 启动多进程服务
gunicorn app.main:app \
  --workers 4 \
  --worker-class uvicorn.workers.UvicornWorker \
  --bind 0.0.0.0:8000
```

### Docker 部署

```dockerfile
FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
```

```bash
# 构建并运行
docker build -t my-fastapi-app .
docker run -d -p 8000:8000 my-fastapi-app
```

---

## 完成确认

### 检查清单

- [ ] 路由器模块化，按业务功能划分
- [ ] 依赖注入封装了数据库会话和用户认证
- [ ] JWT 认证流程正常（登录获取 token，请求携带 token）
- [ ] 数据库 CRUD 操作正常
- [ ] 中间件按预期执行
- [ ] 生命周期事件正确初始化/清理资源
- [ ] 生产环境配置（多进程、环境变量）就绪

### 下一步

查阅 [常见问题](../troubleshooting.md) 了解常见错误的解决方案


path: troubleshooting.md
content: # 常见问题与解决方案

---

## 问题分类说明

**简单问题（1-2步排查）**：安装错误、导入问题等
**中等问题（3-5步排查）**：路由冲突、请求验证失败等
**复杂问题（5-10步排查）**：性能问题、部署故障等

---

## 安装问题

### 1. pip install fastapi 失败【简单问题】

**问题描述**: 使用 pip 安装 FastAPI 时报错

**排查步骤**:
```bash
# 检查 Python 版本（需要 3.8+）
python --version

# 检查 pip 版本
pip --version
```

**常见原因**:
- Python 版本低于 3.8 (40%)
- 网络连接问题 (30%)
- pip 版本过低 (20%)
- 虚拟环境未激活 (10%)

**解决方案**:

**方案A（推荐）**: 使用虚拟环境安装
```bash
python -m venv venv
source venv/bin/activate  # Linux/macOS
# venv\Scripts\activate   # Windows
pip install "fastapi[standard]"
```

**方案B**: 升级 pip 后重试
```bash
python -m pip install --upgrade pip
pip install fastapi
```

---

### 2. uvicorn 命令找不到【简单问题】

**问题描述**: 安装 FastAPI 后运行 `uvicorn` 提示 command not found

**排查步骤**:
```bash
# 检查 uvicorn 是否安装
pip list | grep uvicorn

# 检查 PATH
which uvicorn
```

**常见原因**:
- 未安装 uvicorn (50%)
- 虚拟环境未激活 (30%)
- PATH 未包含 pip 安装目录 (20%)

**解决方案**:

**方案A（推荐）**: 安装完整依赖
```bash
pip install "fastapi[standard]"
```

**方案B**: 使用 python -m 方式运行
```bash
python -m uvicorn main:app --reload
```

---

## 开发问题

### 3. 路由 404 Not Found【中等问题】

**问题描述**: 请求 API 端点返回 404

**排查步骤**:
```bash
# 查看所有已注册路由
python -c "
from main import app
for route in app.routes:
    if hasattr(route, 'methods'):
        print(f'{route.methods} {route.path}')
"
```

**常见原因**:
- 路径拼写错误或缺少前缀斜杠 (35%)
- 路由器未注册到 app (25%)
- HTTP 方法不匹配 (20%)
- 路径参数类型不匹配 (20%)

**解决方案**:

**方案A**: 检查路由注册
```python
# 确保路由器已 include
from fastapi import FastAPI
from routers import users

app = FastAPI()
app.include_router(users.router, prefix="/users")
```

**方案B**: 检查路径参数类型
```python
# 错误：路径顺序导致冲突
@app.get("/users/{user_id}")  # 这个会捕获 /users/me
@app.get("/users/me")

# 正确：具体路径放前面
@app.get("/users/me")
@app.get("/users/{user_id}")
```

---

### 4. 请求体验证失败 422 Unprocessable Entity【中等问题】

**问题描述**: POST/PUT 请求返回 422 验证错误

**排查步骤**:
```bash
# 检查请求体格式
curl -X POST http://localhost:8000/items/ \
  -H "Content-Type: application/json" \
  -d '{"name": "test", "price": 10.5}'
```

**常见原因**:
- 请求体缺少必填字段 (35%)
- 字段类型不匹配 (30%)
- 未设置 Content-Type: application/json (20%)
- Pydantic 模型定义错误 (15%)

**解决方案**:

**方案A**: 检查 Pydantic 模型定义
```python
from pydantic import BaseModel
from typing import Optional

class Item(BaseModel):
    name: str
    price: float
    description: Optional[str] = None  # 可选字段需要默认值
```

**方案B**: 查看详细错误信息
```python
# 422 响应体包含详细的验证错误
# 检查 response.json()["detail"] 获取具体哪个字段出错
```

---

### 5. 异步数据库操作阻塞【中等问题】

**问题描述**: 使用同步数据库驱动导致 API 响应缓慢

**排查步骤**:
```python
# 检查是否在 async 函数中使用了同步操作
import asyncio
# 如果事件循环被阻塞，其他请求会等待
```

**常见原因**:
- 在 async 路由中使用同步数据库驱动 (50%)
- 未使用连接池 (30%)
- CPU 密集型操作在主线程 (20%)

**解决方案**:

**方案A（推荐）**: 使用异步数据库驱动
```python
# 使用 databases 或 SQLAlchemy async
from sqlalchemy.ext.asyncio import create_async_engine

engine = create_async_engine("postgresql+asyncpg://...")
```

**方案B**: 同步操作改用普通函数（FastAPI 自动在线程池运行）
```python
# FastAPI 会自动将 def 路由放入线程池
@app.get("/items/")
def get_items(db: Session = Depends(get_db)):
    return db.query(Item).all()
```

---

## 部署问题

### 6. CORS 跨域请求被拒绝【简单问题】

**问题描述**: 前端请求 API 报 CORS 错误

**排查步骤**:
```bash
# 检查响应头是否包含 CORS 头
curl -I -X OPTIONS http://localhost:8000/api/items \
  -H "Origin: http://localhost:3000"
```

**常见原因**:
- 未添加 CORS 中间件 (60%)
- allowed_origins 配置错误 (25%)
- 预检请求未处理 (15%)

**解决方案**:

```python
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000"],  # 生产环境使用具体域名
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)
```

---

### 7. 生产环境部署 uvicorn workers 配置【中等问题】

**问题描述**: 单 worker 无法充分利用多核 CPU

**排查步骤**:
```bash
# 检查 CPU 核心数
python -c "import multiprocessing; print(multiprocessing.cpu_count())"

# 检查当前 worker 数
ps aux | grep uvicorn
```

**常见原因**:
- 默认单 worker 运行 (50%)
- worker 数设置过多导致内存不足 (30%)
- 未使用进程管理器 (20%)

**解决方案**:

**方案A（推荐）**: 使用 gunicorn + uvicorn workers
```bash
gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker \
  --bind 0.0.0.0:8000
```

**方案B**: 直接使用 uvicorn 多 worker
```bash
uvicorn main:app --workers 4 --host 0.0.0.0 --port 8000
```

---

### 8. 静态文件无法访问【简单问题】

**问题描述**: 挂载的静态文件目录返回 404

**排查步骤**:
```bash
# 确认静态文件目录存在
ls -la static/

# 确认挂载顺序（静态文件应在最后挂载）
```

**常见原因**:
- 目录路径错误 (40%)
- 挂载顺序在 API 路由之前 (30%)
- 文件权限问题 (30%)

**解决方案**:

```python
from fastapi.staticfiles import StaticFiles

# 注意：静态文件挂载应在所有 API 路由之后
app.mount("/static", StaticFiles(directory="static"), name="static")
```

---

## 性能问题

### 9. 依赖注入每次请求重新创建【中等问题】

**问题描述**: 数据库连接等资源每次请求都重新创建，导致性能下降

**排查步骤**:
```python
# 检查依赖函数是否正确使用 yield
# 检查是否使用了 lru_cache 缓存重量级依赖
```

**常见原因**:
- 未使用 yield 管理资源生命周期 (40%)
- 未缓存配置类依赖 (35%)
- 每次请求创建新的数据库引擎 (25%)

**解决方案**:

**方案A**: 使用 yield 依赖管理连接
```python
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()
```

**方案B**: 缓存配置依赖
```python
from functools import lru_cache

@lru_cache
def get_settings():
    return Settings()
```

---

### 10. Swagger 文档页面加载缓慢【简单问题】

**问题描述**: /docs 页面在生产环境加载很慢

**排查步骤**:
```bash
# 检查是否从 CDN 加载 Swagger UI 资源
curl -I https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js
```

**常见原因**:
- CDN 资源在国内访问慢 (60%)
- 路由数量过多导致 OpenAPI schema 太大 (30%)
- 未设置 CDN 镜像 (10%)

**解决方案**:

**方案A**: 使用本地静态文件
```python
from fastapi.openapi.docs import get_swagger_ui_html

@app.get("/docs", include_in_schema=False)
async def custom_swagger_ui():
    return get_swagger_ui_html(
        openapi_url="/openapi.json",
        title="API Docs",
        swagger_js_url="/static/swagger-ui-bundle.js",
        swagger_css_url="/static/swagger-ui.css",
    )
```

**方案B**: 生产环境禁用文档
```python
app = FastAPI(docs_url=None, redoc_url=None)  # 生产环境
```


Security scan verdicts — ClawScan: clean | static analysis: suspicious | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)