30 分钟上手 FastAPI:比 Flask 快、比 Django 轻

📅 2026/7/8 9:19:04
30 分钟上手 FastAPI:比 Flask 快、比 Django 轻
30 分钟上手 FastAPI比 Flask 快、比 Django 轻一、为什么选择 FastAPI如果你想用 Python 写一个 Web API你的选择列表里大概率会出现 Flask、Django 和 FastAPI。Flask 以轻量著称但内置能力有限大部分功能需要插件支撑Django 功能全面但体量庞大写 API 时总有一种杀鸡用牛刀的感觉。FastAPI 恰好填补了这两者之间的空白——它体量轻、性能高并且原生支持异步和自动生成 API 文档。下面这张表能让你直观感受三者的差异特性FlaskDjangoFastAPI框架定位微框架全栈框架高性能 API 框架异步支持需插件需插件原生支持自动文档需插件需 DRF原生 Swagger / Redoc数据校验手动DRF SerializerPydantic 自动校验性能并发一般一般接近 Node.js 水平FastAPI 底层基于StarletteWeb 核心和Pydantic数据校验这让它天生就具备了高性能和类型安全两大优势。接下来我们直接上手写代码用 30 分钟感受它的魅力。二、环境准备在开始之前确保你已安装 Python 3.8 及以上版本。然后创建一个项目目录并安装依赖# 创建项目目录mkdirfastapi-democdfastapi-demo# 创建虚拟环境推荐python-mvenv venv# 激活虚拟环境# Windows 用户venv\Scripts\activate# Mac / Linux 用户sourcevenv/bin/activate# 安装 FastAPI 和 UvicornASGI 服务器pipinstallfastapi uvicorn安装完成后我们来看第一段代码。三、Hello World你的第一个 API创建一个main.py文件写入以下代码# 导入 FastAPI 类fromfastapiimportFastAPI# 创建一个应用实例这个实例是整个 Web 应用的核心appFastAPI()# 定义一个 GET 请求的路由# app.get(/) 是装饰器语法表示当用户访问根路径 / 时执行下面的函数app.get(/)defread_root(): 根路径的接口返回一句欢迎语。 return{message:Hello, FastAPI!}# 定义一个带路径参数的路由# {item_id} 是路径参数FastAPI 会自动从 URL 中提取并传给函数app.get(/items/{item_id})defread_item(item_id:int): 根据物品 ID 查询物品信息。 参数 item_id 的类型注解为 intFastAPI 会自动校验 如果传入的不是数字会返回 422 校验错误。 return{item_id:item_id,name:f物品_{item_id}}启动服务uvicorn main:app--reloadmain是文件名main.pyapp是文件中的 FastAPI 实例名--reload表示启用热重载代码改动后服务自动重启。打开浏览器访问http://127.0.0.1:8000你会看到 JSON 响应{message:Hello, FastAPI!}再访问http://127.0.0.1:8000/items/42你会看到{item_id:42,name:物品_42}如果你尝试访问http://127.0.0.1:8000/items/abcFastAPI 会直接返回一个 422 校验错误因为abc无法转换为int类型。这就是类型注解带来的自动校验能力。四、自动生成的 API 文档FastAPI 最让人惊艳的特性之一就是自动生成交互式 API 文档无需任何额外配置。Swagger UI访问http://127.0.0.1:8000/docsReDoc访问http://127.0.0.1:8000/redoc在 Swagger 页面上你不仅能看到所有接口的列表还能直接点击 “Try it out” 按钮在线调试接口。这比 Postman 还方便因为文档和调试工具天然集成在一起永远不会出现文档和代码不一致的问题。五、请求体与 Pydantic 模型真实项目中接口往往需要接收复杂的 JSON 数据。FastAPI 使用 Pydantic 模型来定义请求体和响应体的结构同时自动完成数据校验。fromfastapiimportFastAPIfrompydanticimportBaseModelfromtypingimportOptional appFastAPI()# 定义数据模型继承自 Pydantic 的 BaseModelclassItem(BaseModel): 物品的数据模型。 每个字段都带有类型注解FastAPI 会根据这些信息 1. 自动生成 JSON Schema 2. 自动校验请求数据 3. 自动生成 API 文档中的示例 name:str# 必填字段字符串类型price:float# 必填字段浮点数类型description:Optional[str]None# 可选字段默认值为 Nonetax:Optional[float]None# 可选字段默认值为 None# 一个模拟的数据库用字典存储物品信息fake_db{}# POST 请求创建物品app.post(/items/)defcreate_item(item:Item): 接收一个 JSON 请求体自动转换为 Item 对象。 如果数据不符合模型定义FastAPI 会返回 422 错误 并明确指出哪个字段有问题。 # 将 Pydantic 模型转为字典方便存到数据库item_dictitem.model_dump()item_idlen(fake_db)1fake_db[item_id]item_dictreturn{item_id:item_id,item:item_dict}# PUT 请求更新物品app.put(/items/{item_id})defupdate_item(item_id:int,item:Item): 更新指定 ID 的物品信息。 路径参数 item_id 和请求体 item 同时使用 FastAPI 能自动区分并分别处理。 ifitem_idnotinfake_db:return{error:物品不存在}fake_db[item_id]item.model_dump()return{item_id:item_id,item:item.model_dump()}用 Python 代码测试一下importrequests# 测试创建物品responserequests.post(http://127.0.0.1:8000/items/,json{name:机械键盘,price:399.0,description:Cherry 轴体87 键布局,tax:39.9})print(response.json())# 输出{item_id: 1, item: {name: 机械键盘, price: 399.0, ...}}# 测试缺少必填字段的情况responserequests.post(http://127.0.0.1:8000/items/,json{name:鼠标}# 缺少 price 字段)print(response.status_code)# 输出422print(response.json())# 输出详细的校验错误信息六、查询参数与参数校验除了路径参数和请求体FastAPI 还支持查询参数URL 中?后面的部分并且可以给参数加上校验规则。fromfastapiimportFastAPI,QueryfromtypingimportOptional appFastAPI()# 模拟的商品数据products[{id:1,name:机械键盘,category:外设,price:399.0},{id:2,name:游戏鼠标,category:外设,price:199.0},{id:3,name:显示器,category:显示设备,price:1999.0},{id:4,name:人体工学椅,category:家具,price:2999.0},{id:5,name:屏幕挂灯,category:照明,price:159.0},]app.get(/products/)deflist_products(# Query 可以给查询参数添加校验规则和描述category:Optional[str]Query(defaultNone,# 默认值为 None表示可选description按商品分类筛选,# 在 API 文档中显示的描述min_length1,# 如果传了值最少 1 个字符),min_price:Optional[float]Query(defaultNone,description最低价格筛选,ge0,# ge greater than or equal大于等于 0),max_price:Optional[float]Query(defaultNone,description最高价格筛选,ge0,),keyword:Optional[str]Query(defaultNone,description商品名称关键词搜索,),page:intQuery(default1,# 默认值为 1description页码,ge1,# 页码最小为 1),page_size:intQuery(default10,# 默认每页 10 条description每页数量,ge1,# 最少 1 条le100,# le less than or equal最多 100 条),): 商品列表接口支持多条件筛选和分页。 所有查询参数都是可选的默认返回全部商品的第一页。 # 第一步按条件筛选resultproductsifcategoryisnotNone:# 筛选出指定分类的商品result[pforpinresultifp[category]category]ifmin_priceisnotNone:# 筛选出价格 min_price 的商品result[pforpinresultifp[price]min_price]ifmax_priceisnotNone:# 筛选出价格 max_price 的商品result[pforpinresultifp[price]max_price]ifkeywordisnotNone:# 筛选出名称中包含关键词的商品result[pforpinresultifkeywordinp[name]]# 第二步计算分页信息totallen(result)# 符合条件的总记录数start(page-1)*page_size# 当前页的起始索引endstartpage_size# 当前页的结束索引page_dataresult[start:end]# 当前页的数据return{total:total,# 总记录数page:page,# 当前页码page_size:page_size,# 每页条数total_pages:(totalpage_size-1)//page_size,# 总页数data:page_data,# 当前页数据}你可以用以下 URL 测试http://127.0.0.1:8000/products/— 获取全部商品http://127.0.0.1:8000/products/?category外设— 只看外设http://127.0.0.1:8000/products/?min_price100max_price500— 价格在 100~500 之间http://127.0.0.1:8000/products/?keyword键盘— 搜索键盘http://127.0.0.1:8000/products/?page1page_size2— 第一页每页 2 条七、依赖注入——FastAPI 的灵魂特性依赖注入Dependency Injection是 FastAPI 最强大的设计之一。你可以把公共逻辑如数据库连接、用户认证、权限校验抽成函数然后在路由中声明依赖FastAPI 会自动调用并把结果注入。fromfastapiimportFastAPI,Depends,HTTPException appFastAPI()# 模拟一个获取当前用户身份的函数# 真实场景中这里会解析 JWT token 或从 session 中获取用户信息defget_current_user(): 获取当前登录用户。 这里模拟返回一个固定用户实际项目中会从请求头中解析 token。 return{user_id:1,username:xiaoming,role:admin}# 依赖函数检查用户是否是管理员defrequire_admin(current_user:dictDepends(get_current_user)): 校验当前用户是否具有管理员权限。 如果不是管理员直接抛出 403 异常阻止后续逻辑执行。 注意这个函数本身也依赖了 get_current_user形成了依赖链。 ifcurrent_user.get(role)!admin:raiseHTTPException(status_code403,# HTTP 状态码禁止访问detail权限不足只有管理员才能访问此接口)returncurrent_user# 普通接口只需要登录即可访问app.get(/users/me)defread_current_user(current_user:dictDepends(get_current_user)): 获取当前登录用户的信息。 通过 Depends(get_current_user) 声明依赖 FastAPI 会自动调用 get_current_user 并将返回值注入到 current_user 参数中。 return{message:当前用户信息,user:current_user}# 管理员接口需要管理员权限才能访问app.get(/admin/dashboard)defadmin_dashboard(admin_user:dictDepends(require_admin)): 管理员仪表盘接口。 这里声明了 require_admin 依赖而 require_admin 又依赖了 get_current_user。 FastAPI 会自动解析这个依赖链按顺序执行。 如果不是管理员require_admin 会抛出 403此函数不会被调用。 return{message:欢迎进入管理员面板,admin_user:admin_user,stats:{total_users:1280,active_today:356,new_orders:42,}}依赖注入的核心优势在于代码复用认证逻辑写一次所有需要认证的接口直接声明依赖即可。依赖链一个依赖可以依赖另一个依赖FastAPI 会自动解析执行顺序。可测试测试时可以方便地替换依赖函数实现 mock。文档自动生成依赖函数中的参数也会出现在 API 文档中。八、异步支持——性能的秘诀FastAPI 原生支持async/await这是它在性能上大幅领先 Flask 和 Django 的关键原因之一。importasynciofromfastapiimportFastAPI appFastAPI()# 模拟一个耗时的 IO 操作比如调用第三方 API 或查询数据库asyncdeffetch_data_from_external_api(resource_id:int): 模拟调用外部 API 获取数据。 asyncio.sleep 模拟网络 IO 等待这个等待期间 CPU 可以去处理其他请求。 awaitasyncio.sleep(2)# 模拟 2 秒的网络延迟return{resource_id:resource_id,data:f资源_{resource_id}_的数据}# 异步接口使用 async def 定义app.get(/async-data/{resource_id})asyncdefget_async_data(resource_id:int): 异步接口示例。 当遇到 await 时FastAPI 会挂起当前协程转去处理其他请求 从而实现高并发。如果多个请求同时到达它们会交替执行 不会因为某个请求阻塞而影响其他请求。 dataawaitfetch_data_from_external_api(resource_id)return{message:异步获取成功,result:data}# 同步接口使用普通的 def 定义app.get(/sync-data/{resource_id})defget_sync_data(resource_id:int): 同步接口示例。 同步函数会被 FastAPI 放到线程池中执行避免阻塞事件循环。 但线程池的并发能力有限高并发场景下性能不如异步方式。 这里的 time.sleep 会阻塞整个线程。 importtime time.sleep(2)# 同步等待 2 秒会阻塞当前线程return{message:同步获取成功,result:f资源_{resource_id}_的数据}关键结论对于IO 密集型操作数据库查询、HTTP 请求、文件读写使用async defawait可以大幅提升并发能力。对于CPU 密集型操作图像处理、复杂计算使用同步函数FastAPI 会自动在线程池中执行。同一个路由中不要混用同步和异步尤其是不要在async def函数中调用同步阻塞函数。九、项目结构建议当项目逐渐变大把所有代码放在一个main.py里显然不可取。推荐按照以下结构组织代码fastapi-project/ ├── app/ │ ├── __init__.py │ ├── main.py # 应用入口FastAPI 实例创建 │ ├── api/ # 路由层 │ │ ├── __init__.py │ │ ├── v1/ │ │ │ ├── __init__.py │ │ │ ├── items.py # 物品相关接口 │ │ │ └── users.py # 用户相关接口 │ ├── models/ # Pydantic 数据模型 │ │ ├── __init__.py │ │ ├── item.py │ │ └── user.py │ ├── services/ # 业务逻辑层 │ │ ├── __init__.py │ │ ├── item_service.py │ │ └── user_service.py │ ├── dependencies/ # 依赖注入 │ │ ├── __init__.py │ │ └── auth.py │ └── core/ # 核心配置 │ ├── __init__.py │ ├── config.py # 配置管理 │ └── database.py # 数据库连接 ├── tests/ # 测试 │ ├── __init__.py │ └── test_items.py ├── requirements.txt └── README.md使用 FastAPI 的APIRouter来拆分路由# app/api/v1/items.pyfromfastapiimportAPIRouter# 创建子路由prefix 设置路径前缀tags 用于 API 文档分组routerAPIRouter(prefix/items,tags[物品管理])router.get(/)deflist_items():获取物品列表return{items:[]}router.post(/)defcreate_item():创建物品return{message:创建成功}# app/main.pyfromfastapiimportFastAPIfromapp.api.v1importitems,users appFastAPI(title我的 FastAPI 项目,description一个结构清晰的 API 服务,version1.0.0,)# 注册子路由app.include_router(items.router)app.include_router(users.router)这样组织代码后每个模块职责清晰多人协作或后期维护都会轻松很多。十、总结这篇文章带你走完了 FastAPI 从零到上手的关键路径知识点核心要点路由定义app.get/post/put/delete装饰器路径参数和查询参数数据校验Pydantic 模型 类型注解自动校验请求体自动文档Swagger UI 和 ReDoc 零配置生成查询参数校验Query()函数提供ge、le、min_length等校验规则依赖注入Depends()实现代码复用和依赖链异步支持async/await提升 IO 密集型场景的并发性能项目结构APIRouter拆分路由分层架构保持代码整洁FastAPI 之所以在 Python Web 框架中后来居上靠的就是写得少、跑得快、文档自动来这三板斧。如果你要从零开始一个 API 项目选 FastAPI 不会错。接下来你可以继续探索数据库集成SQLAlchemy、Tortoise-ORM、身份认证JWT、OAuth2、WebSocket 实时通信、以及 Docker 容器化部署等进阶话题。希望这篇文章能帮你快速迈过 FastAPI 的门槛Happy Coding