关于 FastAPI
FastAPI 是 2018 年新出的 Python Web 框架,专门写 API 接口(不是渲染网页的)。它有 3 个杀手锏:
- 速度——基于 Starlette + Pydantic + 异步,性能接近 NodeJS / Go。
- 类型提示自动校验——你声明
q: int,传字符串就自动 422 报错,不用手写校验。 - 自动生成 API 文档——访问
/docs直接出现 Swagger UI,可以在浏览器里调接口。
安装 FastAPI 和 uvicorn
fastapi 是框架本身(写代码用);uvicorn 是 ASGI 服务器(跑代码用)。Flask/Django 自带开发服务器,FastAPI 必须配一个外部服务器,uvicorn 是官方推荐的。
# 一行装齐: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 里是特殊字符,不加引号会报错。
HelloWorld + 自动 API 文档
新建 /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 起:
$ 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
🎉 重头戏:自动 API 文档
访问 /docs,看到的就是 Swagger UI——所有接口列表 + 在线试用
点开任一接口 → "Try it out" → 填参数 → "Execute",不用 Postman 也能测
/redoc 是另一种风格的文档(更适合阅读,不能在线试),/docs 适合开发调试。两个同时存在,按需用。
路径参数 vs 查询参数
http://api.com/users/42?page=2&size=10 ↑ ↑ Path 路径参数 Query 查询参数 (路径的一部分) (问号后面 key=value)
规则:函数参数怎么写
| 规则 | 解释 |
|---|---|
名字在 {} 里出现过 | FastAPI 当成 Path 参数 |
| 名字没在 URL 里出现 | FastAPI 当成 Query 参数 |
| 有默认值 | 是可选查询参数 |
| 没默认值 | 是必填参数 |
追加到 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
测试一下
user_id: int,FastAPI 就替你做了:参数提取、类型转换、错误处理、生成文档。这一点 Flask/Django 都做不到——它们都得手写校验。
用 Pydantic 模型接收 JSON 请求体
FastAPI 用 Pydantic 模型来接收 body:你声明一个类,它自动校验、转类型、生成文档。
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:
# 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",...}]}
- 函数参数名出现在 URL
{xxx}里 → Path 参数 - 是 Pydantic 模型类型 → Body 请求体
- 都不是 → 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 的差别想清楚。
FastAPI 应用启动需要靠?
访问哪个 URL 可以看 FastAPI 自动生成的交互式文档?
/docs 是 Swagger UI(可在线试),/redoc 是 ReDoc(更适合阅读)。代码 def get_user(user_id: int):,访问 /users/abc 时会发生什么?
FastAPI 中函数返回字典,最终客户端收到的是什么格式?
FastAPI 自带 ORM,可以像 Django 一样定义模型、自动建表。
FastAPI 可以用 async def 写异步接口,处理高并发请求性能更好。
三种参数与 API 设计(6 题)
Path / Query / Body 的判断要熟练。
@app.get("/users/{user_id}") 中 user_id 是哪种参数?
{} 里 → Path 参数。def list_articles(skip: int = 0, limit: int = 10): 中两个参数是否必填?
下面哪种数据应该用 Request Body(POST/PUT 请求体)传?
想用一个类来描述请求体的字段(含校验),应该继承哪个父类?
BaseModel。RESTful 风格里,"创建一篇文章"应该用哪个 HTTP 方法?
在 /docs 页面可以直接点 "Try it out" 在浏览器里调用接口、看返回结果,不用 Postman。