Skip to content

第 1 章:初识 FastAPI ​

学框架最怕「上来就抄代码,抄完不知道怎么坏」。本章先不写复杂业务,把 FastAPI 的底座、能力边界和适用场景讲清楚——知道它为什么快、快在哪,后面 18 章才不容易走弯路。


学习目标 ​

  • 理解 WSGI 与 ASGI 的差异,能说清同步模型在高并发 I/O 场景下到底卡在哪
  • 掌握 FastAPI 的技术底座:Starlette 负责 Web 层、Pydantic 负责数据层,FastAPI 本身是二者的粘合与增强
  • 能逐条列出 FastAPI 的五项核心特性,并解释每一项解决什么问题
  • 能独立跑通最小应用,并说清 /docs、/redoc、/openapi.json 三者的关系
  • 能对比 FastAPI / Flask / Django + DRF,判断自己的项目该不该选 FastAPI

1.1 从 WSGI 到 ASGI ​

Python Web 框架长期建立在 WSGI(Web Server Gateway Interface,PEP 3333)之上:服务器把请求打包成 environ 字典交给应用,应用返回一个可迭代的字节序列。这个接口是同步的——应用函数必须在这一个调用里把响应算完。

同步模型的问题不在于「慢」,而在于等待期间线程被白白占住:

  • 查一次数据库 20 ms、调一次外部 API 200 ms、读一次对象存储 30 ms,这些时间 CPU 什么都没干,但处理请求的线程必须原地阻塞
  • 想提高并发只能加线程或加进程;线程受 GIL 限制、上下文切换有开销,进程则吃内存
  • 结果就是:I/O 越重,单机性价比越低,1 个 worker 撑住 10 个并发请求就要烧掉 10 个线程

ASGI(Asynchronous Server Gateway Interface)把接口改成基于协程的:应用是一个 async 可调用对象,接收 scope、receive、send 三个参数,可以反复 await。这带来两个关键能力:

能力说明
异步 I/O 并发单线程事件循环可以在等待 I/O 时切换去处理其他请求,1 个 worker 轻松撑住成百上千个并发连接
协议可扩展scope["type"] 可以是 http、websocket、lifespan,因此 WebSocket、HTTP/2、长连接天然可支持

⚠️ 关键认知:async 不是「加速器」。它只对 I/O 密集 场景有效;对 CPU 密集计算(图像处理、加解密、复杂算法)没有任何帮助,反而因为单线程事件循环容易被拖垮。选型时先判断你的瓶颈是 I/O 还是 CPU。


1.2 FastAPI 的技术底座 ​

FastAPI 不是从零造轮子,这一点决定了它的稳定性和学习成本。它的依赖只有四个(见官方 pyproject.toml):starlette、pydantic、typing-extensions、typing-inspection。

text
FastAPI  =  Starlette(Web 层)  +  Pydantic(数据层)  +  FastAPI 自己的三样东西
            路由 / 中间件 /       校验 / 序列化 /          1. 类型提示 → 参数的映射规则
            WebSocket / 生命周期   JSON Schema 生成       2. 依赖注入系统(Depends)
                                                          3. OpenAPI 文档自动生成
  • Starlette:一个轻量 ASGI 框架,提供路由、中间件、请求/响应对象、WebSocket、后台任务、lifespan。FastAPI 的 app 本质上就是一个增强了路由与文档能力的 Starlette 应用。
  • Pydantic v2:Python 里事实标准的数据校验与序列化库,核心由 Rust 实现(pydantic-core),所以校验速度比 v1 有量级提升。

理解了这层关系,很多疑问会自动消失:为什么中间件写法和 Starlette 一样?为什么 Request、Response 从 fastapi 导入但其实是 Starlette 的?为什么校验错误是 Pydantic 的格式?


1.3 五项核心特性 ​

1.3.1 类型提示驱动的参数声明 ​

FastAPI 把「函数签名」当作唯一事实来源。你不需要写 @app.route 之外的额外声明,参数的来源与类型全靠注解推导:

python
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
    ...
  • 名字出现在路径模板里 → 路径参数
  • 名字不在路径模板里、且是简单类型 → 查询参数
  • 是 Pydantic 模型 → 请求体

1.3.2 自动数据校验与序列化 ​

item_id: int 不只是给人看的注解。FastAPI 会用 Pydantic 把字符串 "42" 转成整数 42;转不了就返回 422 Unprocessable Entity,并给出字段级错误详情。返回值则被自动序列化为 JSON。

1.3.3 原生 async ​

路径操作函数既可以是 async def,也可以是普通 def。区别很重要:

  • async def:在事件循环里直接 await,适合异步 I/O(如 AsyncSession、httpx.AsyncClient)
  • def:FastAPI 会把它丢到 线程池 执行,不阻塞事件循环,适合调用同步库

1.3.4 自动生成 OpenAPI 文档 ​

启动即拥有 /openapi.json、/docs、/redoc,全部由代码与 Pydantic 模型推导,无需维护独立的接口文档文件。

1.3.5 依赖注入 ​

Depends 让「共用逻辑」(数据库会话、当前用户、分页参数、权限校验)可以声明式地注入,并且天然支持嵌套、缓存与测试覆盖。这是 FastAPI 区别于其他微框架最有价值的一块,第 6 章会专门展开。


1.4 最小可运行示例 ​

本章先用单文件 main.py,第 11 章会重构为 app/ 分层结构。

python
# main.py
from fastapi import FastAPI

app = FastAPI(title="FastAPI 教程 Demo", version="0.1.0")


@app.get("/")
async def read_root():
    return {"message": "Hello World"}


@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
    return {"item_id": item_id, "q": q}

逐行解释 FastAPI 的「读心术」:

代码FastAPI 的解释
app = FastAPI()创建一个 ASGI 应用实例,同时建立内部路由表与 OpenAPI 元数据
@app.get("/")注册一个 路径操作(Path Operation):路径 / + HTTP 方法 GET
item_id 出现在 {item_id} 中声明为路径参数,位置由 URL 模板决定
item_id: int声明类型为 int,运行时自动转换与校验,失败返回 422
q: str | None = None不在路径模板中 → 查询参数;有默认值 → 可选
return {...}字典被 Pydantic 序列化为 JSON 响应,默认状态码 200

启动与验证:

bash
uv run fastapi dev main.py
# 或 pip 环境:fastapi dev main.py
curl "http://127.0.0.1:8000/items/42?q=hello"
# {"item_id":42,"q":"hello"}

curl "http://127.0.0.1:8000/items/abc"
# 422,detail 里说明 int_parsing 失败

1.5 自动文档三件套 ​

端点是什么什么时候用
/openapi.json机器可读的 OpenAPI 3.1 规范(JSON)生成客户端 SDK、导入 Postman / Apifox、CI 里做契约校验
/docsSwagger UI,可交互开发调试首选,能直接填参数发请求
/redocReDoc,只读、三栏布局接口多的时候通读更舒服,适合交付给前端与外部对接方

三者的关系是一份规范、两种渲染:/docs 和 /redoc 都从 /openapi.json 拉数据。所以改代码就是改文档,不存在文档过期问题——这也是 FastAPI 最省事的一点。


1.6 与 Flask / Django + DRF 横向对比 ​

维度FastAPIFlaskDjango + DRF
异步支持原生 ASGI,async/await 是一等公民传统 WSGI 同步;2.x 起支持 async 视图,但周边生态多为同步Django 3.1+ 支持异步视图,ORM 异步能力在 4.1+ 逐步补齐,整体仍以同步为主
数据校验Pydantic 内建,与类型注解同源需自行引入 marshmallow / pydantic / WTFormsDRF Serializer,成熟稳定但字段需手写
自动文档默认生成 OpenAPI + Swagger UI / ReDoc需扩展(APIFlask、flask-smorest)需 drf-spectacular / drf-yasg
ORM不自带,自行选 SQLAlchemy / SQLModel / Tortoise不自带,自行搭配自带 Django ORM + 迁移,一体化
学习曲线低到中:需懂类型注解,异步要理解事件循环低:最小的心智负担高:约定多、概念多,但「开箱即用」程度最高
适合场景API 优先、I/O 密集、微服务、AI/LLM 服务端小工具、原型验证、以模板渲染为主的小站后台管理重的内容型站点、需要全家桶的企业内部系统

取舍要点(不吹不黑):

  • FastAPI 赢在 API 场景:校验、文档、依赖注入、异步四件事组合起来,写接口的样板代码最少。
  • Flask 赢在 简单:几十行的工具,Flask 仍然更直接,不必为类型注解和异步付出认知成本。
  • Django 赢在 一体化:admin、ORM、迁移、认证、模板、表单全都自带。一个内容后台项目,Django admin 能省掉几周的开发量,FastAPI 要从零搭。

1.7 什么时候该选 FastAPI ​

适合:

  • 前后端分离的纯 API 后端,或给移动端 / 小程序提供接口
  • 需要自动文档与客户端 SDK,前后端协作频繁
  • I/O 密集:大量调用外部 API、数据库、对象存储、LLM 服务
  • 微服务中的单个服务,要求镜像小、启动快
  • 已有 Python 数据/算法代码(pandas、PyTorch),要包一层 HTTP 服务

不适合:

  • 以服务端模板渲染 + 后台管理为主的传统内容站:Django 的 admin 与 ORM 是更好的选择
  • CPU 密集是主瓶颈:换框架不解决问题,需要的是多进程、Celery 或原生扩展
  • 团队完全不熟悉类型注解,项目又非常简单:学习成本未必划算
  • 需要开箱即用的完整后台、权限、审计体系:Django 生态现成轮子更多

1.8 错误示范 vs 正确示范 ​

❌ 不推荐:以为 async def 就等于快。

python
import time

import requests
from fastapi import FastAPI

app = FastAPI()


@app.get("/sync-in-async")
async def sync_in_async():
    time.sleep(1)                            # 阻塞事件循环 1 秒
    resp = requests.get("https://example.com")  # 同步 HTTP,同样阻塞
    return {"length": len(resp.text)}

问题:async def 里调用阻塞函数,会卡死整个事件循环——这一个请求等待期间,服务器上所有其他请求都无法被处理,比同步框架更糟。

✅ 推荐:异步路由配异步库;同步库用普通 def 交给线程池。

python
import asyncio

import httpx
from fastapi import FastAPI

app = FastAPI()


@app.get("/async-ok")
async def async_ok():
    await asyncio.sleep(1)                   # 让出事件循环
    async with httpx.AsyncClient() as client:
        resp = await client.get("https://example.com")
    return {"length": len(resp.text)}


@app.get("/use-threadpool")
def use_threadpool():
    import time

    time.sleep(1)                            # 普通 def,FastAPI 丢进线程池执行
    return {"ok": True}

常见坑与排查 ​

现象原因解决
找不到 from fastapi.orm import ... 之类的模块以为 FastAPI 自带 ORMFastAPI 不含 ORM,需自行安装 SQLAlchemy / SQLModel,见第 10 章
并发压测时 QPS 很低,日志显示请求排队在 async def 里写了阻塞调用(requests、time.sleep、同步 DB 驱动)换成 httpx.AsyncClient、asyncio.sleep、异步驱动;或把该路由改成普通 def
照着搜索引擎的代码写 @validator、.dict() 报错搜到的是 Pydantic v1 的旧文档本项目统一 Pydantic v2:@field_validator、model_dump()、model_config = ConfigDict(...)
端口 8000 上打开了别的项目已有进程占用,FastAPI 启动时会报 address already in usefastapi dev main.py --port 8001,或先结束占用进程
访问 /docs 是 404应用实例被创建时传了 docs_url=None,或访问的是错误的前缀检查 FastAPI(...) 参数;带 root_path 部署时确认网关前缀
以为 FastAPI 能直接替代 Django缺少 admin、ORM、模板优先等一体化能力明确项目性质;需要重后台时选 Django,或为 FastAPI 自建管理端
类型注解写了 Optional[str] 也能跑语法可用,但不符合本项目规范统一写 str | None(Python 3.10+ 起原生支持)

本章小结 ​

要点说明
WSGI 的瓶颈同步接口在等待 I/O 时占住线程,I/O 越重并发性价比越低
ASGI 的收益协程化接口,单 worker 可承载大量并发连接,并原生支持 WebSocket
技术底座Starlette 提供 Web 能力,Pydantic v2 提供校验与序列化,FastAPI 负责粘合与增强
五大特性类型提示驱动声明、自动校验序列化、原生 async、自动 OpenAPI、依赖注入
参数来源推导在路径模板中 → 路径参数;简单类型且不在模板中 → 查询参数;Pydantic 模型 → 请求体
文档三件套一份 /openapi.json,/docs 与 /redoc 两种渲染,改代码即改文档
选型结论API 优先、I/O 密集、需要自动文档 → FastAPI;重后台与一体化 → Django;极简小站 → Flask
核心警告async def 不等于快,阻塞调用会拖垮事件循环

练习题 ​

  1. 运行本章的最小示例,用 curl 分别请求 /items/42、/items/42?q=abc、/items/abc,记录三次响应的状态码与 body,并解释 422 响应中 detail[0]["type"] 的含义。
  2. 打开 /docs,用 Swagger UI 的 Try it out 向 /items/{item_id} 发一次请求,然后打开 /openapi.json,找到这个路径对应的 JSON 片段,对照说明 Swagger UI 的表单字段是从哪里来的。
  3. 写一个路由 /slow-async,在 async def 中调用 time.sleep(2);再用浏览器同时打开两个标签页请求它,观察第二个请求是否被阻塞。然后把函数改成普通 def 再测一次,解释差异。
  4. 结合 1.6 与 1.7 的表格,为一个你熟悉的真实项目做选型:写出项目的 I/O 特征、是否需要后台管理、团队技术栈,并给出选 FastAPI 或 Django 的三条理由。

下一章预告 ​

原理讲完了,接下来把环境落地:用 uv 或 venv 装好依赖,跑通热重载开发服务器,并在 Swagger UI 里亲手发出第一个请求。

👉 第 2 章:环境搭建与第一个应用

📖本文阅读--次|📊全站访问--次|👥访客--人