FastAPI 学习笔记:从零搭建分层架构 Demo + 配置 Bug 修复实录
使用 FastAPI + 异步 SQLAlchemy + Pydantic v2 搭建了一个完整的分层架构学习项目,涵盖配置层、数据库层、数据模型、业务逻辑、路由层、依赖注入和测试。顺便还修了一个 Hermes 配置文件的 BOM 隐藏 bug。
前言
这两天主要做了两件事:搭建了一个 FastAPI 分层架构的 Demo 项目来系统学习后端开发,以及修复了一个隐藏的配置文件 BOM 问题。记下来方便以后回顾。
一、FastAPI 分层架构 Demo
之前一直在学 Spring Boot,但 Python 后端的 FastAPI 也是面试和实战中的热门技术栈。正好结合手头的学习需求和 JD 要求(FastAPI/Django/Flask 至少掌握一种),决定从零搭建一个完整的分层架构 Demo。
项目结构
整个项目按职责分层,目录用中文命名,方便直接看出每一层的用途:
FastAPI学习Demo/
├── 启动入口/ # 应用入口,中间件、生命周期、路由注册
├── 配置/ # 全局配置(pydantic-settings)
├── 数据库/ # 异步 SQLAlchemy 引擎、会话工厂
├── 数据模型/ # ORM 表模型 + Pydantic 请求/响应模型
├── 业务逻辑/ # 用户服务、商品服务(Service 层)
├── 路由层/ # API 路由定义
├── 依赖项/ # 依赖注入(DB 会话、分页、JWT 认证)
├── 测试/ # pytest + httpx 异步测试
└── requirements.txt
技术选型
| 组件 | 选择 | 理由 |
|---|---|---|
| Web 框架 | FastAPI | 异步原生、自动生成 OpenAPI 文档、类型安全 |
| ORM | SQLAlchemy 2.x (异步) | 业界标准,支持 async/await |
| 数据库 | SQLite (测试用内存) / PostgreSQL (生产) | 开发轻量,测试隔离 |
| 认证 | JWT (python-jose) | 无状态,RESTful 规范 |
| 密码哈希 | pbkdf2_hmac (标准库) | 零依赖,避开 passlib+bcrypt 兼容坑 |
| 测试 | pytest + httpx | FastAPI 官方推荐方案 |
遇到的坑与解决
坑 1:passlib + bcrypt 版本不兼容
最开始用 passlib 的 CryptContext 做密码哈希,结果 pip 安装后报错:
ValueError: password cannot be longer than 72 bytes
Unable to read bcrypt version
查了一下是 passlib 1.7.4 与 bcrypt 4.0+ 不兼容的老问题。解决方式:改用 Python 标准库 hashlib.pbkdf2_hmac,SHA-256 + 随机盐 + 10 万次迭代,安全级别相当且零外部依赖。
坑 2:路由路径参数不能含中文
FastAPI 路由模板 /{商品ID} 不会匹配中文 URL 路径,所有非 ASCII 参数都会返回 404。最终将路径参数全部改为英文 {item_id},在注释中保留中文说明。
坑 3:Pydantic v2 语法变更
Pydantic v2 弃用了 class Config 语法,改用 model_config = ConfigDict(from_attributes=True)。如果不注意会报 TypeError: Config is not supported in v2。
测试结果
11 个测试用例全部通过:
- 用户注册/登录/JWT 令牌验证
- 获取当前用户信息
- 商品 CRUD(创建、列表、详情、更新、删除)
- 分页查询
- 认证拦截(未登录访问受保护接口 → 401)
pytest 测试/测试接口.py -v → 11/11 passed
项目地址
F:\GitHub_project\FastAPI学习Demo\
二、Hermes 配置文件 BOM 修复
在配置 Hermes Agent 时遇到了一个奇怪的错误:
Failed to parse Hermes config as YAML:
deserializing from YAML containing more than one document is not supported
检查 config.yaml 文件内容并没有 --- 文档分隔符,YAML 结构完全正确。用 Python 的 yaml 库解析也没问题。
问题出在:Windows 的某个编辑器在保存文件时自动加上了 UTF-8 BOM(Byte Order Mark,\xEF\xBB\xBF),这 3 个字节在文件开头。Hermes 底层用的 serde_yaml(Rust 实现)对 BOM 的处理和 Python 的 yaml 库不同——它把 BOM 当成了文档前的垃圾内容,触发了 "多文档 YAML" 的错误检测。
修复方法:用二进制模式读取文件,去掉前 3 个 BOM 字节后重新写入即可。
with open(path, 'rb') as f:
raw = f.read()
if raw.startswith(b'\xef\xbb\xbf'):
raw = raw[len(bom):]
with open(path, 'wb') as f:
f.write(raw)
文件从 9634 字节变回 9631 字节,重启后错误消失。
小结
- FastAPI 的分层架构和 Spring Boot 在思路上非常相似(Controller → Service → Repository),但异步模式和 Python 的依赖注入机制更有 Pythonic 的味道。
- 配置文件的 BOM 问题虽然是个小坑,但不仔细排查很容易被误导——错误信息说的 "多文档" 和实际文件内容完全不匹配,只有从 YAML 解析器的底层行为去理解才能找到根本原因。
- 测试驱动开发(先写测试,再写实现)在这个项目里帮了大忙,每次修改后运行 pytest 就能立刻知道有没有引入回归问题。