FastAPI核心知识点与高频易错点总结
标签:
#FastAPI#Python#后端开发#接口开发#踩坑记录
📑 目录导航
前言
FastAPI 是基于 Starlette(ASGI) 与 Pydantic 构建的高性能 Python Web 接口框架。它依托 Python 类型提示,自动完成参数校验与接口文档生成,兼顾开发效率与运行性能。很多新手在参数传递、异步编程、路由设计、数据校验等方面容易踩坑,本文系统梳理核心知识点与高频开发踩坑点,适合快速复习与面试复盘。
一、核心基础知识点
1.底层两大核心库
- Starlette:轻量级 ASGI 异步 Web 框架,负责请求接收、路由分发、中间件、WebSocket 等底层通信,天然支持高并发。
- Pydantic :数据模型校验库,依靠类型注解完成请求/响应数据的校验与序列化,校验失败时自动返回 422 错误码,无需手写校验逻辑。> 访问内置文档:
/docs(Swagger交互式文档)、/redoc(静态文档),无需手写接口文档。
2.路由与参数区分
(1)路径参数
路径参数定义在 URL 路径的 {} 中,例如 /user/{user_id}。函数参数名必须与花括号内的变量名完全一致 ,否则框架无法正确绑定。```python
from fastapi import FastAPI
app = FastAPI()
@app.get("/user/{user_id}")
def get_user(user_id:int):
return {"id":user_id}
#### (2)查询参数
查询参数位于 URL 的 `?` 之后,以 `key=value` 形式传递,多个参数用 `&` 连接。函数中**不在路径大括号内的参数**会被自动识别为查询参数,且通常应设置默认值或声明为可选。```python
@app.get("/search")
def search(keyword:str|None=None):
return {"kw":keyword}
(3)请求体Post JSON
POST/PUT 等请求的 JSON 请求体,必须使用 Pydantic 的 BaseModel 子类接收。注意:GET 请求没有请求体,不能使用模型接收参数。
python
from pydantic import BaseModel
class Item(BaseModel):
name:str
price:float
@app.post("/item")
def create_item(item:Item):
return item
区分记忆:
- 路径参数:url路径
{xxx}- 查询参数:url问号后面
- 请求体:post json,用BaseModel接收
3. def 和 async def 路由函数(高频考点)
async def:异步函数,内部可以await,适合IO密集操作(异步数据库、http请求),禁止内部写同步阻塞代码(time.sleep、同步数据库),会阻塞事件循环,并发直接垮掉。def普通同步函数:FastAPI自动丢到线程池运行,适合同步ORM、CPU计算,不会阻塞主循环。
4.依赖注入 Depends(FastAPI灵魂)
用来封装公共逻辑:获取数据库会话、登录鉴权、公共参数解析。
python
from fastapi import Depends
def get_db():
"""模拟获取数据库会话"""
db = "db_session"
try:
yield db
finally:
print("关闭连接")
@app.get("/demo")
def demo(db = Depends(get_db)):
return {"db":db}
5.响应模型 response_model
过滤返回字段,做输出序列化,隐藏敏感字段。
python
@app.get("/item/{id}",response_model=Item)
def get_item(id:int):
return {"name":"苹果","price":9.9,"secret":"密钥123"}
# secret字段不会返回给前端
6.跨域中间件CORS
前后端分离必配,否则浏览器拦截请求。
python
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
二、高频易错点(开发踩坑合集)
🚨坑1:422 Unprocessable Entity(最常见)
含义:请求格式正确,但是数据不满足模型/类型校验规则。
常见原因:
- Post接口传form‑data,但是后端用Pydantic模型接收(Pydantic只接收application/json)。
- 参数类型不匹配:定义
int,前端传字符串。 - 必传参数没有传递;
Optional不等于可选参数,必须加=None才可以不传。
python
# ❌错误:Optional只是允许为None,不传参依然报错
def test(name:str|None):
pass
# ✅正确:给默认值None,代表参数可以不传
def test(name:str|None = None):
pass
- Pydantic校验规则不满足(长度、数值范围)。
排查技巧:看返回的detail数组,里面有错误位置与原因;优先在/docs文档页面测试接口。
🚨坑2:路由顺序错误,固定路由被动态路由拦截
固定路径路由必须写在动态路径前面,否则动态参数会优先匹配。
python
# ❌错误
@app.get("/user/{user_id}")
def get_uid(user_id:int):...
@app.get("/user/me")
def get_my_info():... #访问/user/me会被上面路由捕获,user_id="me"报422
# ✅正确:固定路由写上方
@app.get("/user/me")
def get_my_info():...
@app.get("/user/{user_id}")
def get_uid(user_id:int):...
🚨坑3:async def内部写同步阻塞代码
python
import time
# ❌灾难写法,阻塞事件循环,所有请求全部卡住
@app.get("/bad")
async def bad_demo():
time.sleep(2)
return {}
# ✅方案1:改成普通def,交给线程池
@app.get("/good1")
def good_demo():
time.sleep(2)
return {}
# ✅方案2:使用异步sleep
import asyncio
@app.get("/good2")
async def good_demo2():
await asyncio.sleep(2)
return {}
🚨坑4:混淆路径参数与查询参数,参数名不匹配
路由/user/{user_id},函数参数必须叫user_id;如果写id,框架识别不到路径参数,把id当成查询参数,接口直接异常。
🚨坑5:--reload热重载用于生产环境
uvicorn main:app --reload仅本地开发使用;生产环境禁止开启reload,会带来性能与安全隐患,生产使用gunicorn+uvicorn worker部署。
🚨坑6:Pydantic内部普通raise抛出异常返回500
模型校验函数中,不要直接raise ValueError,会返回500服务端错误;需要抛出ValidationError,才返回422参数错误。优先使用Field内置校验规则。
python
from pydantic import BaseModel,Field
class Demo(BaseModel):
num:int = Field(gt=0) #大于0,校验失败返回422
🚨坑7:斜杠歧义 /item 和 /item/
FastAPI会自动重定向,但是项目建议统一路径风格,不要混用末尾斜杠。
🚨坑8:GET接口使用Pydantic接收参数
GET请求没有请求体,Pydantic模型默认读取body,GET接口直接用函数参数接收查询参数。
三、排错小技巧
- 优先访问
/docs在文档页面测试接口,复现问题。 - 出现422,读取返回json的
detail字段,定位哪个字段出错。 - 异步接口卡住,优先检查内部是否存在同步阻塞IO。
- 跨域报错,确认CORS中间件配置,中间件添加位置在
app = FastAPI()之后,路由之前。
四、总结
- FastAPI依靠类型提示,同时完成校验、文档生成;不要省略类型注解,否则校验、文档全部失效。
- 分清路径参数、查询参数、请求体,GET没有body,POST使用Pydantic模型接收JSON。
- 路由顺序:固定路由写动态路由上方。
async def慎用,内部不要写阻塞同步代码;Optional需要搭配=None实现参数可选。- 开发用
--reload,生产环境务必关闭。
配套代码仓库可以把文中示例复制运行,快速复现各个坑点加深理解。