Skip to content

易错点、速查表与自测 ​

初学者常见问题 ​

问题更准确的理解
“返回字典就等于创建成功并保存了”返回数据和写数据库是两件事
“加了类型注解,普通函数就会自动校验”框架和 Pydantic 读取声明并执行校验,普通 Python 不会自动如此
“str | None 一定能不传”是否可省略要看默认值和参数来源
“PATCH 把模型字典全覆盖上去就行”需要区分显式字段与默认值,常用 exclude_unset=True
“async 能让任意计算变快”异步主要帮助等待场景,需要真正异步的调用
“全局事务连接更方便”连接池由 lifespan 管理,事务连接只在自己的业务范围内使用
“CORS 配了就安全了”CORS 是浏览器跨域规则,认证与授权仍由后端完成
“JWT 可以存密码,因为有签名”签名不等于加密,不要存秘密
“后台任务一定执行完”应用进程任务没有天然持久性和自动重试
“数据库模型改完,旧表会自动变化”表结构变化通常需要迁移
“响应模型报错就删掉它”先检查返回数据是否违反接口约定
“多进程共享全局字典”各进程有独立内存,共享业务状态要用外部存储

按现象排查 ​

现象排查顺序
服务没启动读终端错误 → 查 Python 环境 → 查模块与入口
连接被拒绝服务是否运行 → IP/端口是否对 → 网络入口
404路径是否对 → 前缀是否对 → 路由是否登记 → 资源是否存在
405路径可能存在,但 HTTP 方法不匹配
422读 loc → 对字段名与类型 → 对 JSON 外层结构 → 对参数来源
401Token 是否存在 → 格式、签名、过期和声明 → 用户是否有效
403身份已识别,但权限或资源归属是否满足
500读服务器 traceback → 查输出校验、数据库和代码异常
浏览器跨域失败查实际 Origin → 查预检 → 查方法与请求头 → 查 CORS 配置
数据重启后消失是否仍存内存 → 是否提交 → 数据库路径或持久卷是否变化
请求整体卡顿是否阻塞事件循环 → 数据库慢查询 → 外部接口等待 → 资源容量

常用导入速查 ​

python
from typing import Annotated, Literal

from fastapi import (
    APIRouter,
    BackgroundTasks,
    Body,
    Cookie,
    Depends,
    FastAPI,
    File,
    Form,
    Header,
    HTTPException,
    Path,
    Query,
    Request,
    Response,
    UploadFile,
    WebSocket,
    WebSocketDisconnect,
    status,
)
from pydantic import BaseModel, ConfigDict, Field, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict

实际文件只导入用到的内容,不必每次复制全部导入。

核心知识速查 ​

需求常用工具
创建应用FastAPI()
登记接口@app.get()、@app.post() 等
路径范围Annotated[int, Path(gt=0)]
查询校验Annotated[int, Query(ge=1)]
JSON 结构BaseModel
模型字段规则Field(...)
自定义字段规则field_validator
限制公开返回字段response_model=...
预期 HTTP 错误raise HTTPException(...)
复用准备步骤Depends(...)
请求资源清理带 yield 的依赖
应用资源清理lifespan
多文件路由APIRouter + include_router
局部更新字段model_dump(exclude_unset=True)
浏览器跨域CORSMiddleware
表单Form
上传文件UploadFile、File
响应后工作BackgroundTasks
接口测试TestClient + pytest

不看前文,回答这些问题 ​

  1. GET /todos/3?done=true 中,3 与 true 分别来自哪里?
  2. 为什么 JSON 请求体要用模型描述?
  3. name: str | None 与 name: str | None = None 有什么区别?
  4. response_model 为什么能帮助隐藏内部字段?
  5. 找不到任务时为什么要 raise HTTPException?
  6. Depends(get_demo_user) 为什么不写成 Depends(get_demo_user())?
  7. 异步接口中直接使用 time.sleep 会有什么问题?
  8. ORM 表模型与请求模型有什么不同?
  9. Tortoise 的 save 与 refresh_from_db 分别做什么?
  10. PATCH 中为什么不应随意用 exclude_none=True?
  11. 登录成功为什么还需要检查任务归属?
  12. JWT 有签名为什么仍然不能存密码?
  13. CORS 为什么不能代替登录认证?
  14. 测试为什么要隔离数据库?
  15. 为什么生产运行不应使用开发自动重载?

自测参考答案 ​

题号答案要点
13 是路径参数;true 是查询参数
2声明结构、类型和规则,便于校验、文档和编辑器提示
3前者必填但可空;后者可省略且默认为空,模型字段尤其如此
4按公开模型组织和过滤返回数据;直接 Response 需另行处理
5用异常中断正常处理,并生成相应 HTTP 错误响应
6传可调用对象给框架,由它在请求处理时解析参数并调用
7它是同步阻塞操作,可能阻塞事件循环
8一个定义持久表结构,一个定义客户端允许提交的数据
9save 写入对象;refresh_from_db 从数据库重新读取字段;多步提交与回滚由事务管理
10显式 null 可能表示清空字段,不能一律丢掉
11身份认证只证明是谁,访问具体资源仍需授权
12普通签名 JWT 载荷可被读取,签名主要提供完整性校验
13它由浏览器执行跨域规则,不阻止所有客户端调用
14保持可重复性,避免真实数据和测试互相污染
15开发重载和正式进程管理目标不同,正式运行还需资源与重启管理

官方资料的使用方式 ​

先按本手册建立概念,再按遇到的问题查具体章节。查阅时注意所用 Python、FastAPI、Pydantic 和数据库库的版本,不要混用不同年代的写法。

版本与代码验证记录 ​

本次数据库修订使用 Python 3.11、FastAPI 0.143.0、Pydantic 2.13.5、Tortoise ORM 1.1.7、asyncmy 0.2.16;数据库固定为 MySQL 8.0,不使用 8.4 代替。

第 1~14、16~20、22~23 章保留基础示例。新数据库示例、真实 MySQL 验证范围及结果在文末记录;它们用于核对本地教学行为,不代表已做生产负载或线上部署验证。