【无标题】码道: 从零构建一个学生信息管理 API:FastAPI 实战全记录
这里写自定义目录标题
一、写在前面
在前后端分离的开发模式下,后端接口的职责被进一步聚焦:接收请求、校验参数、执行业务逻辑、返回结构化数据。对于想入门 Python Web 开发的同学来说,与其一开始就陷入 Django 那样「全家桶式」的框架复杂度中,不如从一个足够小但五脏俱全的项目入手,亲手把接口开发的完整链路走一遍。
这篇文章将带你完整回顾我最近完成的一个小项目——「学生信息管理系统 API」。它基于 FastAPI 构建,提供了对学生信息的新增、查询、修改、删除(也就是我们常说的 CRUD)能力,数据用内存列表存储,无需安装任何数据库,并且自带 Swagger 交互式文档。整个项目代码已经开源在 AtomGit 仓库(suncy/bigdate_0920)中,这里我把设计思路、实现细节和踩坑过程都记录下来,希望能给同样在入门接口开发的同学一些参考。
二、为什么选择 FastAPI
在动手之前,技术选型是第一个需要回答的问题。Python 生态里做 Web 接口的主流方案无非是 Flask、Django 和 FastAPI 这几类。FastAPI 之所以在近年来迅速流行,主要得益于几个明显优势。
首先是它天生自带优秀的文档能力。FastAPI 基于 OpenAPI 规范,只要按照规范编写代码,它就会自动生成一套可交互的 Swagger 文档和 ReDoc 文档,开发者写完代码的同时,接口说明也就同步产出,省去了大部分手工维护文档的工作。其次是数据校验。FastAPI 深度集成了 Pydantic,我们只需要用「声明式」的方式定义数据模型,例如规定姓名字段长度在 1 到 50 之间、年龄在 1 到 150 之间、性别只能是"男"或"女",框架就会在请求进入业务逻辑之前自动完成校验,非法的输入根本到不了我们的代码里。再次是性能和类型友好,FastAPI 基于 Starlette 和 ASGI 异步框架,性能出众,配合类型提示还可以获得极佳的编辑器补全体验。
对于「学生表的增删改查」这样一个经典的教学需求,FastAPI 几乎是为它量身定做的——代码量小、结构清晰、文档自动生成。因此最终的选择没有任何悬念。
三、项目总体设计
在设计阶段,我首先明确了几个关键决策。
第一,数据存储用内存列表。这是本项目的核心简化之处。真实生产环境中,学生数据毫无疑问应该落在数据库里,但那样会引入数据库安装、连接池、ORM 等一大堆额外的复杂度。作为接口开发的教学项目,把存储层做成一个独立的模块,内部用 Python 的 list 来保存学生对象,配合自增 ID,完全能够演示增删改查的完整逻辑。而且我特意把存储层封装成了一个独立的类,未来想换成 SQLite 或 MySQL 时,只需要修改这一个文件,上层路由代码完全不用动。
第二,接口设计遵循 RESTful 风格。以资源为主体,用 HTTP 方法表达操作语义:GET 负责查询、POST 负责新增、PUT 负责更新、DELETE 负责删除。资源路径统一挂在 /api/v1/students 命名空间下,加上版本号前缀是为了将来接口升级时保持向后兼容。
第三,统一响应格式。业务接口统一返回 {"code": 0, "message": "success", "data": ...} 这样的结构。code 用于表达业务层面的成功与失败,data 承载真正的业务数据。这样前端调用方只需要解析这一个固定的结构,就能处理所有接口,非常利于联调。
四、项目结构是怎么搭的
整个项目的目录结构是一个典型的 FastAPI 分层架构:
bigdate_0920/
├── app/
│ ├── main.py # 应用入口
│ ├── models.py # Pydantic 数据模型
│ ├── storage.py # 内存存储层
│ ├── responses.py # 统一响应与异常处理
│ └── routers/
│ └── students.py # 学生路由
├── tests/
│ └── test_students.py # 单元测试
├── requirements.txt
└── README.md
这个分层可以这样理解:routers 层负责接收 HTTP 请求和返回响应,相当于「门面」;models 层用 Pydantic 定义数据模型,相当于「规格书」;storage 层负责数据的真正存取,相当于「仓库」;而 responses.py 则统一了所有接口的「说话方式」。各层各司其职,逻辑不纠缠,这也是代码能长期维护的基础。
在 main.py 中,我除了创建 FastAPI 实例并挂载路由之外,还做了三件事:配置 Swagger 文档的标题与描述、把根路径重定向到 /docs 方便访问文档、注册三个全局异常处理器,分别是 HTTP 异常、参数校验异常和自定义业务异常,让所有错误都落到统一响应格式中。
五、核心实现详解
5.1 数据模型:用 Pydantic 把规矩定在前面
数据模型是整个接口的「契约」。我定义了四个模型:StudentBase 是公共字段集合,StudentCreate 用于新增,StudentUpdate 用于部分更新,Student 在响应中返回,比基础模型多了 id 和 created_at 两个字段。
在字段约束上,我充分利用了 Pydantic 的能力。比如 name 字段要求 1 到 50 个字符,age 限制在 1 到 150 之间,gender 通过正则 ^(男|女)$ 只允许这两个字面值。此外我还写了一个字段校验器,把纯空格组成的姓名直接拒绝掉。最妙的地方在于 StudentUpdate 模型——它的所有字段都是可选的,配合代码中的 exclude_unset=True,能够实现真正的「部分更新」:客户端传了哪个字段就更新哪个字段,没传的保持原样。
这样做的好处是,所有参数问题都在请求进门那一刻被拦截,业务代码里永远不需要再写一堆 if 判断来防御非法输入。
5.2 存储层:一个线程安全的内存仓库
存储层的实现非常直白但足够严谨。StudentStore 类内部维护一个 list 和一个自增 ID 计数器,每次新增学生时分配新的 ID,再配合 datetime.now() 记录创建时间。
这里有一个容易忽略的细节:并发安全。由于 Uvicorn 是异步多线程运行的,多个请求可能同时操作同一个列表,如果不对共享数据加锁,就可能出现 ID 重复或者读写不一致的问题。我在 StudentStore 里引入了 threading.Lock,凡是修改共享数据(新增、更新、删除、清空)的操作都在锁的保护下进行,确保同一时刻只有一个线程在改动数据。虽然内存存储本身很简单,但这种「多线程访问共享资源要加锁」的意识,是每个后端开发都应该养成的习惯。
5.3 路由层:RESTful 接口的落地
学生路由一共有七个接口:列表查询(支持分页、姓名模糊查询、性别与年龄范围过滤)、单个查询、新增、更新、删除单个、清空全部以及一个健康检查接口。
列表查询是我花心思最多的一个接口。我提供了 page 和 page_size 两个参数实现分页,同时支持 name、gender、age_min、age_max 四个过滤条件。潜在的业务矛盾也考虑到了——如果前端同时传了 age_min=30 和 age_max=18,我会直接返回业务错误码提示参数范围矛盾。分页上限也被限制在 100 条,防止有人恶意传一个 page_size=99999 把内存拖垮。数据按 ID 升序排列后返回,同时返回 total 总数,方便前端做分页器。
清空接口被我设置成了「危险操作」:客户端必须显式携带 confirm=true 参数才会真正清空,否则返回 403。这其实是一个现实工程经验的提前教学——对不可逆操作加一道确认闸门,成本极低,收益却很大。以后接入真实数据库时,这种习惯能避免很多事故。
5.4 统一响应与异常处理
我封装了 ok() 和 fail() 两个工具函数,把所有响应的结构统一起来。还自定义了 BizError 业务异常,代码里只需要 raise BizError(40400, "学生不存在"),全局异常处理器就会自动把它转换成对应的 HTTP 状态码和统一响应体。这样业务代码既简洁又一致,不需要每个路由去关心 HTTP 状态码的映射细节。
这里有一个我在调试中踩到的真实的小坑,值得单独提一下:FastAPI 的 JsonResponse 默认不认识 Python 的 datetime 对象,直接序列化会抛出 TypeError: Object of type datetime is not JSON serializable。解决办法是使用 FastAPI 提供的 jsonable_encoder 先把数据转换成可序列化的结构。自动化测试很快就帮我发现了这个问题,测试的存在价值在这一刻体现得淋漓尽致。
六、Swagger 文档:写完代码就有了接口说明
项目启动后,默认访问 http://127.0.0.1:8000/docs 就能看到 Swagger 交互式文档。所有接口按「学生管理」「系统」标签分组展示,每个接口都带有入参说明、请求示例、响应结构。最方便的是文档支持在线调试——点击任意接口右上角的 Try it out,填好参数点 Execute,就能直接看到真实的调用结果和响应体,连 Postman 都不用开。
这是 FastAPI 最吸引人的特性之一:接口文档不是「额外的工作」,而是写代码过程中的自然产物。因为每个接口的 summary、description 和参数说明都是我在定义路由时随手写上去的,改代码和改文档永远是同步的,永远不会出现「文档已经过期」的尴尬。
七、测试与验证
项目不能光能跑,还得能证明它跑得对。我用 Pytest 和 FastAPI 自带的 TestClient 编写了 22 个单元测试,完全不依赖真实网络请求,测试直接向内存中的应用实例发请求,速度极快。
测试用例覆盖了几个维度:正常路径(增删改查全流程)、边界校验(空姓名、非法性别、越界年龄)、业务规则(年龄范围矛盾、分页上限)、错误路径(查询不存在的学生返回 404、清空未带确认参数返回 403)、以及幂等性回归(多次创建 ID 自增正确)。最终结果一行命令 pytest tests/ -q 输出 22 passed,这种把验证动作自动化、可重复执行的做法,是代码质量最朴素也最可靠的保障。
在自己手动验证的完整流程中,我依次执行了新增两名学生、列表查询、按 ID 查询、部分更新年龄、删除一名学生、用非法性别新增并观察被 422 正确拦截,全程响应业务码正确、数据关系清晰,整个演示一气呵成。
八、怎么把它跑起来
使用这个项目非常轻量:先 git clone 仓库,然后 pip install -r requirements.txt 安装依赖(FastAPI、Uvicorn、Pydantic),再执行 uvicorn app.main:app --reload 启动服务,浏览器打开 /docs 就可以开始体验了。全部依赖只有几个纯 Python 包,从安装到出文档基本不超过两分钟。
九、从这个项目出发还能做什么
这个项目刻意把存储层做成了「易替换」的形态,这为后续的扩展留下了很好的想象空间。如果你想让数据真正持久化,有几个递进的方向:先用 Python 标准库自带的 SQLite 把存储层换成文件型数据库,成本极低;再进阶可以使用 SQLAlchemy 配合 PostgreSQL 或 MySQL;当学生数据量上来之后,还可以引入分页在数据库层的 LIMIT/OFFSET、建立 age 等字段的索引、把接口拆分为服务模块等。更进一步,可以加 JWT 认证、接入 Redis 做缓存、用 Docker 打包部署,甚至编写 CI 流水线把测试和发布自动化。
内存列表 + RESTful 接口的组合虽然简单,但它把接口开发最核心的骨架完整地呈现了出来。骨架一旦搭好,往上面长肌肉只是时间问题。
十、总结
这个项目虽然只有几百行代码,却完整覆盖了一个后端接口项目应有的要素:架构分层、数据模型、参数校验、RESTful 设计、统一响应、异常处理、自动文档、自动化测试。它不追求复杂,而是追求「结构完整、逻辑清晰、能跑能测」。
如果你正在学习 Python Web 开发,不妨从克隆这个仓库开始,试着给它加一个新接口,或者把内存存储换成 SQLite,甚至重新实现一个「图书管理 API」「商品管理 API」。亲手改过的代码才是真正学会的代码。整个项目代码见 AtomGit 仓库 suncy/bigdate_0920,欢迎 clone、star、提 issue。
欢迎使用Markdown编辑器
你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。
新的改变
我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:
- 全新的界面设计 ,将会带来全新的写作体验;
- 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
- 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
- 全新的 KaTeX数学公式 语法;
- 增加了支持甘特图的mermaid语法1 功能;
- 增加了 多屏幕编辑 Markdown文章功能;
- 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
- 增加了 检查列表 功能。
功能快捷键
撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
查找:Ctrl/Command + F
替换:Ctrl/Command + G
合理的创建标题,有助于目录的生成
直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。
如何改变文本的样式
强调文本 强调文本
加粗文本 加粗文本
标记文本
删除文本
引用文本
H2O is是液体。
210 运算结果是 1024.
插入链接与图片
链接: link.
图片:
带尺寸的图片:
居中的图片:
居中并且带尺寸的图片:
当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。
如何插入一段漂亮的代码片
去博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.
// An highlighted block
var foo = 'bar';
生成一个适合你的列表
- 项目
- 项目
- 项目
- 项目
- 项目1
- 项目2
- 项目3
- 计划任务
- 完成任务
创建一个表格
一个简单的表格是这么创建的:
| 项目 | Value |
|---|---|
| 电脑 | $1600 |
| 手机 | $12 |
| 导管 | $1 |
设定内容居中、居左、居右
使用:---------:居中
使用:----------居左
使用----------:居右
| 第一列 | 第二列 | 第三列 |
|---|---|---|
| 第一列文本居中 | 第二列文本居右 | 第三列文本居左 |
SmartyPants
SmartyPants 是一个文本转换工具,主要功能是将普通的 ASCII 标点符号自动转换为更美观的印刷体标点符号。例如:
| 原始符号 | 转换后 | 说明 |
|---|---|---|
"引号" | “引号” | 直引号变弯引号 |
'单引号' | ‘单引号’ | 直单引号变弯单引号 |
-- | – | 两个连字符变短破折号 |
--- | — | 三个连字符变长破折号 |
... | … | 三个点变省略号 |
创建一个自定义列表
-
Markdown
- Text-to- HTML conversion tool Authors
- John
- Luke
如何创建一个注脚
一个具有注脚的文本。2
注释也是必不可少的
Markdown将文本转换为 HTML。
KaTeX数学公式
您可以使用渲染LaTeX数学表达式 KaTeX:
Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n−1)!∀n∈N 是通过欧拉积分
Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=∫0∞tz−1e−tdt.
你可以找到更多关于的信息 LaTeX 数学表达式here.
新的甘特图功能,丰富你的文章
- 关于 甘特图 语法,参考 这儿,
UML图表
可以使用UML图表进行渲染,例如下面产生的一个序列图:
- 关于 UML图表 语法,参考 这儿,
流程图
- 关于 Mermaid 语法,参考 这儿,
FLowchart流程图
我们依旧会支持flowchart.js的流程图语法:
- 关于 Flowchart流程图 语法,参考 这儿.
导出与导入
导出
如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。
导入
如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。
注脚的解释 ↩︎
更多推荐


所有评论(0)