FastAPI路由报ValidationError的根本原因是Pydantic模型校验在请求进入函数前拦截了类型不匹配的输入,如路径参数item_id: int传入"abc"直接返回422错误,而非执行函数体。

FastAPI 为什么一写路由就报 ValidationError
根本原因不是你写错了路径,而是 Pydantic 模型校验在后台默默拦下了不符合类型提示的输入。比如函数参数标了 int,但前端传了字符串 "123",FastAPI 默认会尝试强转;可一旦转失败(比如传了 "abc"),就直接抛 ValidationError,而不是进你的函数体。
- 常见错误现象:
422 Unprocessable Entity+ 一堆value is not a valid integer字段报错 - 调试建议:先用
curl -X POST -H "Content-Type: application/json" -d '{"id":"abc"}' http://localhost:8000/item复现,再看响应体里的detail字段定位哪个字段崩了 - 别急着关校验——这是 FastAPI 的核心优势,关了等于退化成 Flask。真要宽松,改用
Optional[int]或自定义Field(default=None, ...) - 注意:路径参数(如
/items/{item_id})也走校验,item_id: int时传/items/abc一样 422
async def 和 await 在 FastAPI 路由里到底什么时候必须加
只有当你调用了真正异步的 I/O 操作时才需要——比如 await database.fetch_one(...)、await httpx.AsyncClient().get(...)。同步代码(如字典操作、datetime.now()、纯计算)塞进 async def 不仅没提速,反而增加调度开销。
- 常见错误现象:写了
async def read_item(),但函数里全是return {"name": "foo"}—— 这属于“假异步”,协程对象被直接返回,FastAPI 会报TypeError: object dict can't be used in 'await' expression - 正确姿势:
async def路由 +await真异步调用;纯同步逻辑用普通def路由更干净 - 数据库场景:SQLAlchemy Core 支持异步(
asyncpg驱动),但 ORM 的session.query(...).first()是同步的,得换session.execute(...).scalars().first()再await - 性能影响:混用同步阻塞调用(如
time.sleep(1))在async def里会卡死整个事件循环,千万别试
Pydantic v2 的 BaseModel 和 FastAPI 的 Body 怎么配着用才不丢字段
字段丢失通常不是模型写错了,而是 FastAPI 默认把 Body 当作“整个请求体”,而你可能漏了嵌套结构或没设默认值。比如前端传 {"user": {"name": "a", "age": 25}},但模型定义是 class User(BaseModel): name: str; age: int,直接当参数用就会 422。
- 使用场景:需要接收 JSON 对象的深层结构时,必须显式用
Body包一层,例如user: User = Body(..., embed=True)表示期望{"user": {...}}这种格式 - 参数差异:
embed=False(默认)要求请求体直接是{"name": "...", "age": ...};embed=True才匹配带外层 key 的结构 - 容易踩的坑:模型字段设了
Field(default=None),但没加default_factory或None类型提示,FastAPI 会认为该字段必填,前端不传就报错 - 兼容性注意:Pydantic v2 中
Optional[str]等价于str | None,但str = None是非法语法,必须写成name: str | None = None
启动时报 Address already in use 或根本连不上 localhost:8000
FastAPI 本身不绑定端口,靠 uvicorn 启动,所以问题全在 Uvicorn 配置或系统端口占用上。不是代码问题,别翻路由定义。
立即学习“Python免费学习笔记(深入)”;
- 常见错误现象:
ERROR: [Errno 48] Address already in use—— 说明 8000 端口被占了,可能是上次Ctrl+C没杀干净,或者另一个项目正在跑 - 快速检查:
lsof -i :8000(macOS/Linux)或netstat -ano | findstr :8000(Windows),找到 PID 后kill -9 PID或任务管理器结束 - 启动命令别硬编码端口:
uvicorn main:app --reload --port 8001换个端口临时避让;生产环境务必加--host 0.0.0.0,否则默认只监听127.0.0.1,Docker 或局域网访问不到 - 注意:VS Code 的 Python 扩展有时会偷偷起一个调试进程占端口,关掉所有终端再试










