Python FastAPI 项目脚手架
创建符合分层架构的 Python FastAPI 项目,支持多前端应用分组。
参考文档
详细说明请参阅 references/ 目录:
- 项目结构详解 - 架构设计、目录说明、各层职责
- API 设计规范 - RESTful 设计、路由规范、状态码
- 安全指南 - 认证授权、密码安全、限流策略
项目结构
app/
├── __init__.py
├── main.py # 应用入口
│
├── api/ # API 层 - 按前端分组
│ ├── __init__.py # 路由注册入口
│ ├── admin/ # 后台管理端 API
│ ├── mp/ # 小程序端 API
│ └── health.py # 健康检查(不分组)
│
├── schemas/ # 数据验证层 - 按前端分组
│ ├── admin/
│ ├── mp/
│ └── common/
│
├── models/ # 数据模型层 - 所有前端共享
├── services/ # 业务逻辑层 - 所有前端共享
│
├── core/ # 核心配置层
│ ├── config.py # 配置管理
│ ├── deps.py # 依赖注入
│ ├── security.py # 密码加密、JWT
│ ├── auth.py # 认证依赖
│ ├── permissions.py # 权限控制
│ ├── exceptions.py # 全局异常处理
│ ├── logging.py # 日志配置
│ └── middleware/ # 中间件
│ ├── rate_limit.py # 限流中间件
│ └── request_logging.py # 请求日志
│
├── db/ # 数据库层
│ ├── base.py # SQLAlchemy Base
│ ├── session.py # 数据库会话管理
│ └── redis_client.py # Redis 客户端(支持 db0~db15)
│
└── tests/ # 测试目录
├── conftest.py # 测试配置
├── api/ # API 测试
└── services/ # 业务逻辑测试
数据库支持
| 数据库 | 驱动 | 连接示例 |
|---|---|---|
| PostgreSQL | asyncpg | postgresql+asyncpg://user:pass@host:5432/db |
| MySQL | aiomysql | mysql+aiomysql://user:pass@host:3306/db |
| SQLite | 内置 | sqlite:///./app.db |
Redis 多数据库
支持 db0~db15,按用途分库:
| 数据库 | 用途 |
|---|---|
| db0 | 缓存 |
| db1 | 会话 |
| db2 | 限流 |
| db3 | 消息队列 |
| db4 | 分布式锁 |
安全配置
| 功能 | 文件 | 说明 |
|---|---|---|
| JWT 认证 | core/security.py |
Token 生成与验证 |
| 用户认证 | core/auth.py |
获取当前用户依赖 |
| 权限控制 | core/permissions.py |
RBAC 角色权限 |
| 限流 | middleware/rate_limit.py |
基于 IP+指纹 的限流 |
| 异常处理 | core/exceptions.py |
全局异常捕获 |
| 日志 | core/logging.py |
请求日志、错误日志 |
限流策略
IP + 浏览器指纹组合限流
为什么需要浏览器指纹?
如果只用 IP 限流,局域网内的所有用户会共享同一个 IP,导致:
- 一个人请求过多,整个局域网的人都被误封
- 无法区分同一 IP 下的不同客户端
解决方案:
使用 IP + 浏览器指纹 + 路径 作为限流键:
rate_limit:{ip}:{fingerprint}:{path}
浏览器指纹生成:
从请求头中提取特征信息组合生成指纹:
- User-Agent: 浏览器类型、版本、操作系统
- Accept-Language: 语言偏好设置
- Accept-Encoding: 支持的编码方式
相同浏览器 + 相同设置 = 相同指纹
不同浏览器或不同设置 = 不同指纹
效果:
- 局域网内不同用户(不同浏览器)独立计数
- 同一用户在不同设备上独立计数
- 避免误封正常用户
使用方式
步骤 1:询问用户需求
创建前确认:
- 项目名称和路径
- 需要哪些前端应用(admin/mp/app/web)
- 使用哪种数据库(PostgreSQL/MySQL/SQLite)
- 是否需要 Redis
步骤 2:复制模板
cp -r assets/templates/app <目标路径>/app
cp -r assets/templates/tests <目标路径>/tests
cp assets/templates/requirements.txt <目标路径>/
cp assets/templates/.env.example <目标路径>/.env
cp assets/templates/pyproject.toml <目标路径>/
cp assets/templates/.gitignore <目标路径>/
步骤 3:按需调整
根据用户选择:
- 删除不需要的前端目录(如不需要 mp 则删除 api/mp/、schemas/mp/)
- 修改 .env 中的 DATABASE_URL
- 如不需要 Redis,可删除 db/redis_client.py
步骤 4:更新路由注册
修改 api/__init__.py,只包含用户需要的应用。
新增路由流程
- 在对应目录创建
xxx.py,定义router = APIRouter() - 在该目录的
__init__.py中 import 并 include - 完成,无需修改其他文件
认证使用示例
from fastapi import APIRouter, Depends
from app.core.auth import get_current_active_user
from app.core.permissions import check_permission, Permission
from app.models.user import User
router = APIRouter()
@router.get("/protected")
def protected_endpoint(current_user: User = Depends(get_current_active_user)):
return {"user": current_user.username}
@router.get("/admin-only")
def admin_only(current_user: User = Depends(check_permission(Permission.SYSTEM_WRITE))):
return {"message": "Admin access granted"}
Redis 使用示例
from app.db import redis_client
# 缓存
redis_client.cache.set("key", "value", ex=3600)
redis_client.cache.get("key")
# 会话
redis_client.session.set("session:123", "user_data")
# 限流
redis_client.rate_limit.incr("rate:user:123")
# 分布式锁
lock = redis_client.lock.lock("my_lock", timeout=10)
运行测试
# 安装测试依赖
pip install pytest pytest-cov
# 运行测试
pytest
# 运行测试并生成覆盖率报告
pytest --cov=app --cov-report=html