1. 路径参数与查询参数的混淆¶
错误场景:定义路由时参数类型未声明,或误用路径参数/查询参数。
错误代码:
# 错误:路径参数未声明类型,FastAPI无法推断
@app.get("/users/{user_id}")
async def get_user(user_id):
return {"user_id": user_id}
原因:FastAPI依赖参数类型声明来解析请求,未声明类型会导致参数解析失败(如user_id被当作字符串处理,或前端传数字时报错)。
正确代码:
# 正确:路径参数声明类型为int
@app.get("/users/{user_id}")
async def get_user(user_id: int):
return {"user_id": user_id}
解决方法:
- 路径参数(URL中{}包裹的参数)必须在函数参数中声明类型(如user_id: int)。
- 查询参数(URL中?key=value形式)可通过Query类设置默认值(如user_id: Optional[int] = None)。
2. 请求体与查询参数的误用¶
错误场景:用GET请求传复杂数据,或POST请求未使用请求体。
错误代码:
# 错误:GET请求用查询参数传“复杂数据”(实际应放在请求体)
@app.post("/items")
async def create_item(item_id: int, name: str):
return {"item_id": item_id, "name": name}
原因:GET请求的参数默认放在查询字符串,不适合传复杂数据;POST请求应通过请求体(Pydantic模型)传递数据。
正确代码:
# 正确:用Pydantic模型定义请求体
from pydantic import BaseModel
class Item(BaseModel):
item_id: int
name: str
@app.post("/items")
async def create_item(item: Item):
return item
解决方法:
- 简单筛选/过滤用查询参数(如/items?name=apple)。
- 复杂数据(如对象、嵌套结构)用POST+Pydantic模型作为请求体。
3. Pydantic模型使用错误¶
错误场景:模型字段类型错误,或忘记导入BaseModel。
错误代码:
# 错误1:模型字段类型错误(字符串写成int)
class User(BaseModel):
name: int # 前端传字符串会报错
age: int = 18
# 错误2:未导入BaseModel导致模型定义无效
class Product(BaseModel): # 报错:BaseModel未定义
id: int
原因:Pydantic模型需严格定义字段类型,且必须继承BaseModel。
正确代码:
from pydantic import BaseModel
class User(BaseModel):
name: str # 正确类型:字符串
age: int = 18 # 可选字段带默认值
class Product(BaseModel):
id: int
解决方法:
- 确保模型字段类型与前端传参类型一致(如str/int/float)。
- 必传字段不设默认值,可选字段用Optional[type] = None或直接设默认值。
4. 状态码使用不当¶
错误场景:成功返回用200(如创建资源),或错误状态码未显式设置。
错误代码:
# 错误:创建资源用200状态码(应为201)
@app.post("/users")
async def create_user(user: User):
return user # 状态码默认200,不符合REST规范
原因:RESTful API中,201 Created表示资源创建成功,200 OK通常表示读取/更新成功。
正确代码:
from fastapi import status
@app.post("/users", status_code=status.HTTP_201_CREATED)
async def create_user(user: User):
return user # 返回201状态码
解决方法:
- 创建资源用201,读取用200,删除用204 No Content。
- 错误场景(如资源不存在)用HTTPException指定状态码(如404 Not Found):
from fastapi import HTTPException
@app.get("/users/{user_id}")
async def get_user(user_id: int):
if user_id not in [1, 2, 3]:
raise HTTPException(status_code=404, detail="用户不存在")
5. 跨域资源共享(CORS)配置缺失¶
错误场景:前后端分离时,前端调用API出现“跨域错误”。
错误代码:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items")
async def get_items():
return {"items": [1, 2, 3]}
原因:浏览器默认禁止跨域请求(前端域名与后端不同),需配置CORS中间件。
正确代码:
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# 允许所有前端域名(生产环境建议指定具体域名)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"], # 允许所有HTTP方法(GET/POST等)
allow_headers=["*"], # 允许所有请求头
)
@app.get("/items")
async def get_items():
return {"items": [1, 2, 3]}
解决方法:
- 开发环境用allow_origins=["*"]快速调试,生产环境替换为前端实际域名(如["https://your-frontend.com"])。
6. 异步与同步函数混用¶
错误场景:在异步依赖中使用同步函数未处理,或同步函数标记为异步。
错误代码:
# 错误:异步函数调用同步库未加await
async def get_db():
db = create_connection() # 同步库(如SQLite)
return db # 错误:未await同步函数
@app.get("/items")
async def get_items(db=Depends(get_db)):
return db.query("SELECT * FROM items") # 报错:同步库未等待
原因:FastAPI异步函数中调用同步库需用await或转换为异步(如用asyncpg代替psycopg2)。
正确代码:
import asyncio
async def get_db():
loop = asyncio.get_event_loop()
db = await loop.run_in_executor(None, create_connection) # 同步库异步执行
return db
解决方法:
- 优先使用异步依赖库(如asyncpg、httpx.AsyncClient)。
- 若必须用同步库,用asyncio.run_in_executor在异步上下文中调用。
7. 依赖注入与中间件错误¶
错误场景:依赖函数未返回、中间件导入错误。
错误代码:
# 错误1:依赖函数未返回yield(未用异步上下文管理器)
def get_db(): # 同步依赖未yield
db = create_connection()
return db # 错误:未yield导致资源未释放
# 错误2:中间件导入错误(如用flask的CORS而非fastapi的)
from flask import Flask
from fastapi import FastAPI
app = FastAPI()
app.add_middleware(Flask.middleware.cors.CORS) # 导入错误
原因:依赖注入需用yield处理资源释放,中间件需导入FastAPI的对应模块。
正确代码:
from fastapi import Depends, FastAPI
from contextlib import asynccontextmanager
# 正确:依赖注入用yield释放资源
@asynccontextmanager
async def get_db():
db = create_connection()
try:
yield db
finally:
db.close() # 资源释放
@app.get("/items")
async def get_items(db=Depends(get_db)):
return db.query("SELECT * FROM items")
解决方法:
- 异步依赖用@asynccontextmanager标记,同步依赖用yield或async def。
- 中间件导入FastAPI自带模块(如CORSMiddleware而非Flask的)。
8. 文档访问问题¶
错误场景:Swagger UI或ReDoc无法访问(如路由未注册)。
错误代码:
from fastapi import APIRouter
router = APIRouter() # 未添加到app
@app.get("/items")
async def get_items():
return [1, 2, 3]
原因:FastAPI自动生成文档需路由被注册到应用实例。
正确代码:
from fastapi import FastAPI, APIRouter
app = FastAPI()
router = APIRouter() # 路由定义
@router.get("/items")
async def get_items():
return [1, 2, 3]
app.include_router(router) # 注册路由到app
@app.get("/users") # 直接装饰的路由也会生成文档
async def get_users():
return ["user1", "user2"]
解决方法:
- 路由用@app.get直接装饰或通过APIRouter.include_router注册。
- 启动服务后访问http://localhost:8000/docs查看Swagger UI。
总结¶
FastAPI的错误多源于“参数解析”“类型匹配”和“REST规范”的疏忽。新手可通过以下方式避免:
1. 优先参考官方文档(https://fastapi.tiangolo.com)和示例代码。
2. 开发时先写简单功能验证逻辑(如路径参数、状态码)。
3. 遇到问题检查:参数类型声明、请求体/查询参数使用、状态码是否符合REST规范。
随着实践增多,这些“坑”会逐渐转化为经验,让你的API开发更高效!