从零搭建学生信息管理系统 API:FastAPI + 内存存储 + Swagger 完整实践

在学习和日常开发中,我们经常需要快速搭建一个可供前后端联调、供同事演示的接口服务。传统做法是选择 Django 或 Flask,前者重、后者裸,两者都需要额外配置数据库才能跑起来,而很多时候我们想要的只是一个"能快速验证接口设计是否合理"的轻量服务。本文将以一个"学生信息管理系统"为例,完整记录如何使用 FastAPI 在十分钟内搭建一个具备增删改查能力、自带 Swagger 交互式文档的 RESTful API 服务。

本文项目地址:https://atomgit.com/LJQ060817/bigData_0919

一、为什么选择 FastAPI

在动手之前,先回答一个最关键的问题:为什么选 FastAPI?目前 Python 生态里主流的 Web 框架主要有三个,各有取舍。

Django 是最"重"的全家桶框架,自带 ORM、Admin 后台、模板引擎和认证体系,适合大型业务系统,但代价是学习曲线陡峭、启动成本高,创建一个最小项目就要生成大量的样板文件。Flask 则走到了另一个极端,它足够轻、足够灵活,但很多能力需要自己组装,比如参数校验、序列化、文档生成都需要引入第三方扩展才能获得较好体验。FastAPI 恰好站在两者的中间地带,它解决了 Python Web 开发中被诟病多年的两个痛点:

第一,性能。FastAPI 基于 Starlette 和 Pydantic,底层走的是异步事件循环,官方基准测试中与 Node.js、Go 属于同一梯队,远超传统的 Flask。

第二,开发体验。FastAPI 依赖 Python 3.6+ 的类型注解(Type Hints)实现了"声明即校验、声明即文档":你在代码里写下的类型注解,同时决定了请求参数如何校验、数据如何序列化,以及 OpenAPI 文档如何生成。这意味着你不需要额外维护一份接口文档,Swagger UI 和 ReDoc 会自动从你的代码中生成,接口和文档永远不会失同步。

对于我们要做的学生信息管理系统,FastAPI 的自动文档能力简直是量身定做——写完代码,文档就"免费"送到了我们面前。

二、项目设计与结构

明确了技术选型之后,我们先来设计这个小项目。需求非常清晰:对学生表进行增删改查,用内存列表存储数据,需要 Swagger API 说明。围绕这几个需求,我设计了如下数据模型。

一名学生包含这些字段:姓名(name)、年龄(age)、性别(gender)、邮箱(email)、专业(major)、入学年份(enrollment_year),另外服务端自动生成全局唯一的编号(id)和创建时间(created_at)。性别限定为"男"“女”"保密"三个枚举值,年龄限定在 1 到 150 之间,入学年份限定在 2000 到 2100 之间——这些约束不是写在注释里的口头约定,而是通过 Pydantic 的 Field 声明直接成为运行时校验规则。

项目采用模块化结构,把不同职责拆到不同文件:

bigData_0919/
├── app/
│   ├── main.py              # FastAPI 应用入口与文档配置
│   ├── models.py            # Pydantic 数据模型
│   ├── database.py          # 内存数据库实现
│   └── routes/
│       └── students.py      # 学生信息路由
├── requirements.txt
└── README.md

这样拆分的价值在于:模型的变更不会影响存储层,存储层的替换不会影响路由层。以后若想从内存列表换成 MySQL,只需改写 database.py,接口层一行代码都不用动。

三、核心实现详解

3.1 数据模型:声明即校验

在 models.py 中,我用 Pydantic 定义了三层模型:StudentBase(公共字段)、StudentCreate(新增请求体,继承公共字段)、StudentUpdate(更新请求体,所有字段可选)、Student(响应模型,含 id 和 created_at)。

这里最精髓的是 StudentUpdate 的设计。更新操作往往允许只修改部分字段,比如只改年龄、不动专业。我把它设计为"所有字段均可选",在存储层使用 model_dump(exclude_unset=True) 只提取用户显式传入的字段,实现真正的部分更新(Partial Update)。这也符合 RESTful 设计中对 PUT 语义的常见处理方式。

3.2 内存存储:线程安全是关键

database.py 是整个项目数据逻辑的核心。我的内存数据库用一个字典 self._students 以"id → 学生对象"的形式保存数据,配合自增计数器 self._next_id 生成主键,并额外维护一个 threading.Lock 锁。

为什么要加锁?因为 Uvicorn 默认会启动多线程处理并发请求,如果两个请求同时往字典里写数据,可能出现"读到的数据不一致"甚至"主键冲突"的问题。用 with self._lock 把"生成 id + 写入字典"这两个步骤包成一个原子操作,就保证了并发安全。虽然只有几十行代码,但它完整模拟了真实数据库需要解决的问题:原子性、并发控制、主键分配。这也是一份很好的"从内存到数据库"的过渡教材。

查询方面,列表接口支持三个维度:按姓名或邮箱关键字模糊搜索(keyword)、按专业精确筛选(major)、分页(skip/limit)。模糊搜索用 if keyword.lower() in s.name.lower() 实现大小写不敏感匹配,专业筛选用精确相等比较,最后按分页参数切片返回。

3.3 路由层:贯彻 RESTful 语义

routes/students.py 中定义了五组接口,全部挂在 /api/v1/students 前缀下:

方法路径语义成功状态码
POST/api/v1/students新增学生201 Created
GET/api/v1/students查询列表(筛选+分页)200 OK
GET/api/v1/students/{id}查询单个学生200 OK
PUT/api/v1/students/{id}更新学生200 OK
DELETE/api/v1/students/{id}删除学生204 No Content

特别说明两个容易被忽视的细节。第一,状态码的选取:新增资源返回 201 Created,删除资源返回 204 No Content(无响应体),这两个细节是很多初学者容易忽略的,但恰恰是 RESTful API 规范性的体现。第二,404 的处理:查询或删除一个不存在的 id,统一抛出 HTTPException(status_code=404),让客户端能明确区分"参数错误"(422)和"资源不存在"(404)两种情况。

3.4 应用装配与文档配置

main.py 负责把路由挂载到应用上,并配置文档信息:

app = FastAPI(
    title="学生信息管理系统 API",
    description="...",
    version="1.0.0",
)

FastAPI 会根据这些信息自动生成三份文档资源:/docs(Swagger UI,一个可以在浏览器里直接点击"Try it out"进行调试的交互式页面)、/redoc(ReDoc,更适合阅读的 API 参考文档)、/openapi.json(机器可读的 OpenAPI 规范,可直接导入 Postman、Apifox 等工具)。此外我还加了一个 / 根路径做健康检查,返回服务名称、版本和当前内存中的学生总数。

四、Swagger 文档:不写文档的文档方案

这是本项目最有特色的部分。传统团队维护接口文档通常有两种方式:手写 Markdown(极易过期)或用 Postman 等工具导出一份快照(更新麻烦)。而 FastAPI 的文档是从代码自动生成的,代码即文档,两者天然保持一致。

打开 http://127.0.0.1:8000/docs,你会看到:

  • 接口列表:按 tag 分组展示所有接口,每个接口都带摘要(summary)和详细描述(description);
  • 参数说明:路径参数、查询参数、请求体都有字段级说明,且标注了必填性、数据类型、默认值;
  • 校验规则可视化age 的 1~150 范围、gender 的枚举值、字符串长度限制全部自动展示;
  • 在线调试:点击"Try it out",填入参数即可直接向真实服务发请求,响应体和状态码一目了然。

这意味着接口联调阶段的效率会大幅提升:前端同学不再追着后端问"字段叫什么、必填吗、报错是什么格式",打开 /docs 自己就能调。我认为这是 FastAPI 最打动开发者的特性,也是本文强调"Swagger API 说明"的真正落地方式。

五、启动与测试

使用前先安装依赖:

pip install -r requirements.txt

启动服务:

uvicorn app.main:app --host 0.0.0.0 --port 8000

然后可以用 curl 做一轮完整的增删改查验证。新增学生:

curl -X POST http://127.0.0.1:8000/api/v1/students \
  -H "Content-Type: application/json" \
  -d '{"name":"张三","age":20,"gender":"男","major":"计算机科学与技术","enrollment_year":2023}'

服务端返回 201 和完整的学生对象(含自动生成的 id 和 created_at)。查询列表、按关键字搜索、更新年龄、删除学生,对应的请求示例在 README 中都有完整清单。

再故意传入一个非法参数测试校验能力,比如把 age 设为 300,服务端返回 422,并给出结构化的错误信息:错误类型(less_than_equal)、出错字段位置(body.age)、错误描述和接收到的非法值。这一整套校验体系零手动代码,全部来自类型注解,充分体现了"声明式编程"的魅力。

六、常见问题与踩坑记录

在开发调试这个项目的过程中,有几个坑值得记录下来,供后来者避雷。

第一个坑是中文查询参数的编码问题。直接使用 curl 发送带中文的 URL(如 ?keyword=张三)时,终端会把未编码的中文字符直接放进请求行,导致服务端收到 “Invalid HTTP request”,因为 HTTP 协议要求 URL 中的非 ASCII 字符必须进行百分号编码。正确做法是用 curl -G --data-urlencode "keyword=张三" 让 curl 自动完成编码。这提醒我们:任何客户端工具在拼接 URL 时都要正确处理编码,前端使用 fetch/axios 时库通常会自动处理,但手写命令行时最容易踩中这个坑。

第二个坑是进程重启数据丢失。内存存储的代价就是生命周期与进程绑定,服务重启、修改代码后热重载、部署平台回收容器,都会清空数据。所以我在 README 和项目注释中都作了明确提示。实际使用中如果学生数据需要保留,应尽早把存储层替换为真正的数据库,这也是我们刻意把存储隔离在 database.py 一个文件里的原因。

第三个坑是容易混淆的 HTTP 状态码。初学者常把所有失败都统一返回 400,但规范和历史经验告诉我们应当区分语义:422 表示请求参数校验失败,客户端可据此定位表单字段并修复;404 表示资源不存在,客户端应提示用户"目标数据已被删除"。把这两个语义区分开,前后端联调时才能减少大量不必要的沟通成本。

第四个坑是跨域问题(CORS)。如果前端是单独部署的网页应用(比如 Vue/React 构建的工程),浏览器出于安全策略会拦截跨域请求,需要在后端配置 CORSMiddleware 并放行相应的来源地址。本项目只提供纯接口,没有配套页面,所以暂未配置 CORS,但这是一个"页面端 + API 端"分离架构下必然会遇到的问题,值得提前了解。

这些坑都不大,但每一个都对应真实开发中会遇到的典型问题,记录下来希望能帮助读者少走弯路。

七、设计思考与扩展方向

这个项目虽然小,但包含了几个值得沉淀的设计思想。

第一,内存存储是刻意为之。它让学习者绕开数据库配置的干扰,专注理解接口设计本身。数据层被隔离在 database.py 中,这意味着项目有明确的可扩展路径:替换为 SQLAlchemy 即可接 MySQL/PostgreSQL,替换为 MongoEngine 即可接 MongoDB,路由和模型层完全不受影响。

第二,部分更新的语义。StudentUpdate 全部字段可选的设定,配合 exclude_unset=True,让接口在"全量替换"与"部分更新"之间取得了清晰且可预期的行为,这在真实业务中非常常见。

第三,错误反馈的规范性。404 和 422 的明确区分,为前端错误处理提供了可靠依据:捕获 422 做表单校验提示,捕获 404 提示"数据不存在"。

如果要继续扩展,这个项目可以自然生长出很多能力:接入 SQLite 实现数据持久化;用 JWT 加一层管理员鉴权;增加年级、班级等关联模型,形成一对多接口;用 pytest + httpx 补上完整的自动化测试;用 Dockerfile 打包交付,一条命令部署到服务器。每一个扩展点都已有清晰的位置可落脚。

八、总结

本文从零实现了一个学生信息管理系统 API:用 FastAPI 承载接口,用内存列表完成增删改查,用 Pydantic 完成声明式校验,用自动生成的 Swagger UI 免去了手写文档的负担。整个过程代码量精简、结构清晰、文档完备,非常适合作为 FastAPI 的入门项目或团队内部的接口原型脚手架。

技术浪潮变化很快,但"清晰的接口设计、可靠的参数校验、规范的错误反馈、自动化的文档产出"这些工程诉求是永恒的。FastAPI 用类型注解这一现代 Python 特性,把"写代码"和"写文档"这两件事合二为一,让开发者可以把省下来的时间投入到真正的业务逻辑上。希望这篇文章能给你带来启发,也欢迎到项目仓库查看完整代码并动手实践。@TOC

欢迎使用Markdown编辑器

你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

新的改变

我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:

  1. 全新的界面设计 ,将会带来全新的写作体验;
  2. 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
  3. 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
  4. 全新的 KaTeX数学公式 语法;
  5. 增加了支持甘特图的mermaid语法1 功能;
  6. 增加了 多屏幕编辑 Markdown文章功能;
  7. 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
  8. 增加了 检查列表 功能。

功能快捷键

撤销: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.

图片: Alt

带尺寸的图片: Alt

居中的图片: Alt

居中并且带尺寸的图片: Alt

当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。

如何插入一段漂亮的代码片

博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.

// An highlighted block
var foo = 'bar';

生成一个适合你的列表

  • 项目
    • 项目
      • 项目
  1. 项目1
  2. 项目2
  3. 项目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)=(n1)!nN 是通过欧拉积分

Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t   . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=0tz1etdt.

你可以找到更多关于的信息 LaTeX 数学表达式here.

新的甘特图功能,丰富你的文章

2014-01-07 2014-01-09 2014-01-11 2014-01-13 2014-01-15 2014-01-17 2014-01-19 2014-01-21 已完成 进行中 计划一 计划二 现有任务 Adding GANTT diagram functionality to mermaid
  • 关于 甘特图 语法,参考 这儿,

UML图表

可以使用UML图表进行渲染,例如下面产生的一个序列图:

王五 李四 张三 王五 李四 张三 李四想了很长时间, 文字太长了 不适合放在一行. 你好!李四, 最近怎么样? 你最近怎么样,王五? 我很好,谢谢! 我很好,谢谢! 打量着王五... 很好... 王五, 你怎么样?
  • 关于 UML图表 语法,参考 这儿,

流程图

链接

长方形

圆角长方形

菱形

  • 关于 Mermaid 语法,参考 这儿,

FLowchart流程图

我们依旧会支持flowchart.js的流程图语法:

Created with Raphaël 2.3.0 开始 我的操作 确认? 结束 yes no
  • 关于 Flowchart流程图 语法,参考 这儿.

导出与导入

导出

如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。

导入

如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。


  1. mermaid语法说明 ↩︎

  2. 注脚的解释 ↩︎

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐