084、FastAPI 入门:异步 Web 框架的类型驱动开发、自动文档与验证

📅 2026/6/29 21:57:57
084、FastAPI 入门:异步 Web 框架的类型驱动开发、自动文档与验证
084、FastAPI 入门异步 Web 框架的类型驱动开发、自动文档与验证从一次线上事故说起去年有个项目后端用Flask写的API上线第三天就炸了。用户传了个嵌套JSON某个字段应该是int结果传了个字符串123Flask的request.get_json()直接原样接收业务层没做类型校验数据库写入时报错整个接口500。更坑的是前端说“我传了数字啊”后端说“我收到的是字符串”两边扯皮半天。后来查日志才发现是某个中间代理层把JSON重新序列化了一次int变成了字符串。这种问题在FastAPI里几乎不会发生。为什么因为FastAPI从设计上就强制你定义类型而且自动帮你做校验和转换。今天这篇笔记就聊聊FastAPI怎么用类型驱动开发解决这类实际问题。类型驱动开发不只是语法糖很多人觉得Python的类型注解就是个装饰写不写无所谓。但在FastAPI里类型注解是核心机制。看个最简单的例子fromfastapiimportFastAPI appFastAPI()app.get(/items/{item_id})asyncdefread_item(item_id:int):return{item_id:item_id}这里item_id: int不是摆设。如果你请求/items/abcFastAPI会直接返回422错误告诉你参数类型不对。而Flask里你得自己写int(item_id)还得处理ValueError。踩坑记录有次我写了个接口参数类型写成item_id: int None结果传了数字也报错。后来发现None默认值会让FastAPI认为这个参数是可选的但类型又是int逻辑矛盾。正确的写法是item_id: Optional[int] None或者直接用item_id: int 0给个默认值。Pydantic模型把数据校验交给框架FastAPI的请求体验证依赖Pydantic。定义一个模型就像给数据画了个模板frompydanticimportBaseModelclassItem(BaseModel):name:strprice:floatis_offer:boolFalse# 别这样写默认值给个字符串falsePydantic不会帮你转这个模型会自动做几件事类型转换传price: 19.99自动转成float校验name不能少price必须是数字文档生成自动生成OpenAPI schema实战经验有次我定义了个复杂嵌套模型里面有个字段是List[Dict[str, Any]]结果前端传了个空数组Pydantic直接报错。后来改成List[Dict[str, Any]] []才解决。记住Pydantic对空值很敏感该给默认值就给。自动文档调试利器FastAPI启动后访问/docs就能看到Swagger UI。这个功能在调试阶段特别有用可以直接在页面上测试接口不用Postman参数类型、必填项一目了然响应模型自动展示返回结构别这样写有人为了省事把所有参数都写成str然后在函数里手动转换。这样自动文档就废了前端看不到真实类型Swagger UI上全是字符串输入框调试效率极低。异步处理别滥用asyncFastAPI支持异步但很多人一上来就给所有函数加async。实际上如果你的函数里没有await调用加async反而有性能损耗# 别这样写纯CPU计算也加asyncapp.get(/compute)asyncdefcompute():resultsome_cpu_intensive_task()# 没有awaitasync白加了return{result:result}# 正确做法同步函数就用defapp.get(/compute)defcompute():resultsome_cpu_intensive_task()return{result:result}踩坑记录有次我写了个异步函数里面调用了同步的数据库驱动结果整个接口阻塞了。后来发现异步函数里如果有同步IO操作最好用run_in_executor包装一下或者直接用同步写法。依赖注入解耦的好工具FastAPI的Depends机制很强大但别滥用。常见场景是共享数据库连接fromfastapiimportDependsdefget_db():dbSessionLocal()try:yielddbfinally:db.close()app.get(/users/{user_id})asyncdefread_user(user_id:int,db:SessionDepends(get_db)):userdb.query(User).filter(User.iduser_id).first()returnuser个人建议依赖注入适合横切关注点比如认证、日志、数据库会话。别把业务逻辑也塞进依赖里否则代码会变得难以追踪。错误处理别让用户看500FastAPI默认的错误响应是JSON格式但你可以自定义fromfastapiimportHTTPExceptionapp.get(/items/{item_id})asyncdefread_item(item_id:int):ifitem_idnotinitems:raiseHTTPException(status_code404,detailItem not found)returnitems[item_id]别这样写有人直接在函数里return {error: not found}状态码还是200。前端拿到200以为成功了解析数据才发现错误。正确做法是用HTTPException让状态码和错误信息一致。个人经验总结类型注解是契约写接口时先定义好Pydantic模型再写业务逻辑。模型就是你和前端之间的合同谁违约谁负责。文档是副产品别为了文档而文档类型定义好了文档自然就有了。Swagger UI是调试利器但别依赖它做自动化测试。异步不是万能药只有IO密集型场景才用asyncCPU密集型用同步。混用时注意线程池和事件循环的配合。错误处理要统一写个全局异常处理器把Pydantic的ValidationError、数据库的IntegrityError都转成统一的JSON响应。这样前端不用猜错误格式。版本管理要趁早项目一开始就用/api/v1/前缀后面升级API时直接加/api/v2/避免破坏现有客户端。最后说句实在话FastAPI的学习曲线比Flask陡但一旦上手开发效率和代码质量都会提升一个档次。特别是团队协作时类型定义就是最好的文档比写几十页接口文档管用多了。