第11章 · FastAPI 框架基础

不写网页,专写接口。FastAPI 用 Python 类型提示自动校验数据 + 自动生成 API 文档,前后端分离就用它。

🎯 写一个商品 API,浏览器自动看到交互式文档

关于 FastAPI

FastAPI 是 2018 年新出的 Python Web 框架,专门写 API 接口(不是渲染网页的)。它有 3 个杀手锏:

  • 速度——基于 Starlette + Pydantic + 异步,性能接近 NodeJS / Go。
  • 类型提示自动校验——你声明 q: int,传字符串就自动 422 报错,不用手写校验。
  • 自动生成 API 文档——访问 /docs 直接出现 Swagger UI,可以在浏览器里调接口。
💡 应用场景
Flask/Django 适合做完整网站(含页面)。FastAPI 适合做纯接口——给前端 Vue/React、移动 App、小程序提供数据。第15章我们就用 FastAPI 给微信小程序做后台。
1 安装

安装 FastAPI 和 uvicorn

💬 为什么要装两个包
fastapi 是框架本身(写代码用);uvicorn 是 ASGI 服务器(跑代码用)。Flask/Django 自带开发服务器,FastAPI 必须配一个外部服务器,uvicorn 是官方推荐的。
SHELL
# 一行装齐:fastapi 是框架,uvicorn[standard] 是 ASGI 服务器
$ pip3 install fastapi "uvicorn[standard]" -i https://pypi.tuna.tsinghua.edu.cn/simple

# 验证
$ python3 -c "import fastapi; print(fastapi.__version__)"
0.111.0
⚠️ 引号别忘
"uvicorn[standard]" 必须带引号。中括号在很多 shell 里是特殊字符,不加引号会报错。
2 第一个程序

HelloWorld + 自动 API 文档

新建 /home/fastapi/main.py

PYTHON /home/fastapi/main.py
# ============ FastAPI 最小程序 ============
from fastapi import FastAPI

# 1. 创建 FastAPI 实例(类似 Flask 的 app = Flask(__name__))
app = FastAPI()


# 2. 用装饰器注册路由
# @app.get("/") 表示:GET 请求 / 时调用下面这个函数
@app.get("/")
def read_root():
    # 直接 return 字典,FastAPI 自动转成 JSON
    return {"message": "Hello, FastAPI!"}


# 3. 再来一个带参数的接口(路径里的 {item_id} 是占位符)
@app.get("/items/{item_id}")
def read_item(item_id: int):
    # 注意 item_id: int —— 这是 Python 类型提示
    # FastAPI 会自动把 URL 里的字符串转成 int
    # 如果转不成(比如访问 /items/abc)会自动返回 422 错误
    return {"item_id": item_id, "price": 9.99}

启动方式不一样

FastAPI 不能直接 python main.py。要用 uvicorn 起:

SHELL
$ cd /home/fastapi
# main 是文件名(main.py),app 是文件里的实例对象名
# --reload 改代码自动重启,--host 0.0.0.0 让外网能访问
$ uvicorn main:app --reload --host 0.0.0.0 --port 8000
INFO:     Uvicorn running on http://0.0.0.0:8000

测试三个 URL

GET http://IP:8000/ → {"message": "Hello, FastAPI!"}
GET http://IP:8000/items/42 → {"item_id": 42, "price": 9.99}
GET http://IP:8000/docs ★ 自动生成的交互式文档

🎉 重头戏:自动 API 文档

访问 /docs,看到的就是 Swagger UI——所有接口列表 + 在线试用

点开任一接口 → "Try it out" → 填参数 → "Execute",不用 Postman 也能测

💡 还有 /redoc
/redoc 是另一种风格的文档(更适合阅读,不能在线试),/docs 适合开发调试。两个同时存在,按需用。
3 Path & Query

路径参数 vs 查询参数

💬 一图看清两种参数
http://api.com/users/42?page=2&size=10
                   ↑                ↑
              Path 路径参数     Query 查询参数
              (路径的一部分)   (问号后面 key=value)

规则:函数参数怎么写

规则解释
名字在 {} 里出现过FastAPI 当成 Path 参数
名字没在 URL 里出现FastAPI 当成 Query 参数
有默认值可选查询参数
没默认值必填参数

追加到 main.py

PYTHON main.py(追加)
# ============ Path 参数:URL 路径里的一部分 ============
@app.get("/users/{user_id}")
def get_user(user_id: int):
    # user_id 是 Path 参数(因为名字在 URL 的 {} 里出现)
    # 类型 int —— 自动校验,传 abc 就会 422
    return {"user_id": user_id, "name": f"用户#{user_id}"}


# ============ Query 参数:URL 问号后面的 key=value ============
@app.get("/articles/")
def list_articles(skip: int = 0, limit: int = 10):
    # skip 和 limit 都是 Query 参数(名字没在 URL 里出现)
    # 都有默认值 → 可选;没传就是 0 和 10
    fake_db = [{"id": i, "title": f"文章{i}"} for i in range(100)]
    return fake_db[skip:skip+limit]


# ============ Path + Query 混着用 ============
@app.get("/users/{user_id}/articles")
def user_articles(user_id: int, q: str = None):
    # user_id 是 Path(在 {} 里),q 是 Query(不在)
    # q: str = None 表示是可选的字符串参数
    result = {"user_id": user_id}
    if q:
        result["search"] = q       # 有传 q 才加进结果
    return result

测试一下

GET/users/42→ Path 参数
GET/users/abc→ 自动 422 错误
GET/articles/?skip=20&limit=5→ Query 参数
GET/users/42/articles?q=python→ Path + Query
💡 自动校验是 FastAPI 最香的特性
你只是写了 user_id: int,FastAPI 就替你做了:参数提取、类型转换、错误处理、生成文档。这一点 Flask/Django 都做不到——它们都得手写校验
4 Request Body

用 Pydantic 模型接收 JSON 请求体

💬 什么时候用 Body
创建 / 修改资源时(POST / PUT),数据通常装在请求体(body)里以 JSON 形式发送,不放在 URL 上——因为 URL 长度有限,且参数多了不好读。
FastAPI 用 Pydantic 模型来接收 body:你声明一个类,它自动校验、转类型、生成文档。
PYTHON main.py(追加)
from pydantic import BaseModel


# ============ 1. 定义数据模型 ============
# 继承 BaseModel,每个属性都有类型注解
class Item(BaseModel):
    name: str                       # 必填字段:商品名
    price: float                    # 必填字段:单价
    description: str = None          # 可选字段(有默认值)
    in_stock: bool = True             # 可选字段,默认 True


# ============ 2. POST 接口:接收 JSON 请求体 ============
@app.post("/items/")
def create_item(item: Item):
    # item 是 Pydantic 模型实例,可以直接 .name / .price 访问
    # FastAPI 已经自动完成:JSON 解析 → 类型校验 → 缺字段报 422

    # 简单逻辑:算一个含税总价(演示用)
    total = item.price * 1.13
    return {
        "name": item.name,
        "price_with_tax": round(total, 2),
        "in_stock": item.in_stock,
    }


# ============ 3. Path + Body 同时用 ============
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
    # item_id 来自 URL(Path),item 来自请求体(Body)
    # FastAPI 自动分辨:基本类型 = Path/Query,Pydantic 模型 = Body
    return {"item_id": item_id, "updated": item.dict()}

怎么测?两种方式

方式一:浏览器打开 /docs(推荐,最快)——找到 POST /items/ → Try it out → 填 JSON → Execute。

方式二:用命令行 curl

SHELL
# POST 一个商品
$ curl -X POST "http://IP:8000/items/" \
       -H "Content-Type: application/json" \
       -d '{"name":"键盘","price":299.0}'
{"name":"键盘","price_with_tax":337.87,"in_stock":true}

# 缺字段会自动 422,根本进不到函数
$ curl -X POST "http://IP:8000/items/" \
       -H "Content-Type: application/json" \
       -d '{"price":299.0}'
{"detail":[{"loc":["body","name"],"msg":"field required",...}]}
⚠️ 关于 GET 不能带 body
HTTP 协议规定:GET 请求不应该带请求体。要传数据用 POST/PUT/PATCH/DELETE。查询数据用 GET + Query;提交数据用 POST + Body——这是 RESTful 风格的核心约定。
🎯 三种参数的判断口诀
  1. 函数参数名出现在 URL {xxx} 里 → Path 参数
  2. 是 Pydantic 模型类型 → Body 请求体
  3. 都不是 → Query 查询参数(默认值决定必填还是可选)

📋 本节小结

FastAPI 全章命令清单

  • pip3 install fastapi "uvicorn[standard]" — 安装
  • uvicorn main:app --reload --host 0.0.0.0 — 启动
  • http://IP:8000/docs — 在线调试 API

API 设计的常用方法约定

方法语义例子
GET读 / 查询列表 / 详情
POST新建创建商品 / 提交订单
PUT整体更新编辑商品全部字段
DELETE删除删商品 / 注销账户

FastAPI 跟另两个框架的核心差异

  • 没有模板——FastAPI 不渲染 HTML,只返回 JSON。
  • 没有 ORM——要操作数据库自己装 SQLAlchemy / Tortoise / 直接用 PyMySQL。
  • 类型提示就是文档——你写 q: int,FastAPI 替你做:校验、转换、错误响应、文档生成。

课堂练习

两组共 12 题:先 FastAPI 基础,再参数与 API 设计。

第一组

FastAPI 基础(6 题)

把启动方式和它跟 Flask/Django 的差别想清楚。

Q1单选

FastAPI 应用启动需要靠?

Apython main.py
Buvicorn main:app --reload 这种 ASGI 服务器
Cflask run
D双击 .py 文件
解析:FastAPI 自己不带服务器,必须配 ASGI 服务器跑(uvicorn / hypercorn / daphne)。这跟 Flask/Django 自带 dev server 不一样。
Q2单选

访问哪个 URL 可以看 FastAPI 自动生成的交互式文档?

A/admin
B/docs
C/swagger.json
D/help
解析:/docs 是 Swagger UI(可在线试),/redoc 是 ReDoc(更适合阅读)。
Q3单选

代码 def get_user(user_id: int):,访问 /users/abc 时会发生什么?

A函数收到 user_id="abc"
B程序崩溃
CFastAPI 自动返回 422,函数根本不执行
D返回 None
解析:类型校验在框架层就完成了——不符合就直接 422,开发者不用写校验代码。这是 FastAPI 的核心卖点。
Q4单选

FastAPI 中函数返回字典,最终客户端收到的是什么格式?

AHTML 页面
B纯文本
CJSON
DXML
解析:FastAPI 默认把 dict / list / Pydantic 模型自动序列化成 JSON。
Q5判断

FastAPI 自带 ORM,可以像 Django 一样定义模型、自动建表。

解析:FastAPI 不带 ORM。要操作数据库需自己装 SQLAlchemy / Tortoise / Beanie 等。它专注做 API 接口本身。
Q6判断

FastAPI 可以用 async def 写异步接口,处理高并发请求性能更好。

解析:FastAPI 同时支持普通 def 和 async def。要调异步库(如 httpx、aiomysql)就用 async def。同步代码用普通 def 就行——FastAPI 会自动放到线程池里执行。
第二组

三种参数与 API 设计(6 题)

Path / Query / Body 的判断要熟练。

Q1单选

@app.get("/users/{user_id}")user_id 是哪种参数?

APath 路径参数
BQuery 查询参数
CBody 请求体
DHeader 头
解析:名字出现在 URL 的 {} 里 → Path 参数。
Q2单选

def list_articles(skip: int = 0, limit: int = 10): 中两个参数是否必填

A都必填
B都可选(有默认值)
Cskip 必填,limit 可选
D只能在 URL 里通过位置传
解析:有默认值的参数都是可选的,没传就用默认值。
Q3单选

下面哪种数据应该用 Request Body(POST/PUT 请求体)传?

A分页页码
B关键字搜索
C用户 ID
D注册新用户的完整信息(姓名、邮箱、密码、地址...)
解析:字段多 / 涉及创建或修改 / 含敏感数据 → 用 Body。少量过滤 / 排序参数用 Query。资源唯一标识用 Path。
Q4单选

想用一个类来描述请求体的字段(含校验),应该继承哪个父类?

Afastapi.RequestBody
Bdict
Cpydantic.BaseModel
Dobject
解析:FastAPI 用 Pydantic 做数据校验,模型类继承 BaseModel
Q5单选

RESTful 风格里,"创建一篇文章"应该用哪个 HTTP 方法?

AGET
BPOST
CPUT
DDELETE
解析:GET 读,POST 创建,PUT 整体更新,PATCH 局部更新,DELETE 删除。
Q6判断

/docs 页面可以直接点 "Try it out" 在浏览器里调用接口、看返回结果,不用 Postman。

解析:Swagger UI 是带交互的——开发联调时极为高效。这也是 FastAPI 让前后端协作变快的关键之一。