1208 字
约 4 分钟
7
S10-J02 · Flask ↔ FastAPI:迁移与对照
适用读者:要在两个框架之间做迁移/共存决策的人。
前置:S10-B04、S10-B05。
实测环境:FastAPI 0.162.0 / Flask 3.1.3 / Python 3.12.3(两框架对同一请求的差异为实测)。
覆盖旧文:—(E 驱动)
覆盖扩展:E-S10-011(同步 vs 异步 handler)、E-S10-012(校验位置差异)。
上一篇:S10-J01 · 依赖治理:requirements / environment.yml / 锁版本
下一篇:S10-A01 · Web 框架选型 ADR。
1. 一句话结论
迁移的难点不在语法,在"错误语义"和"校验位置"两件事——它们会直接影响前端与调用方。
| 维度 | Flask | FastAPI | 迁移要改什么 |
|---|---|---|---|
| 参数校验 | 手写 / 第三方 | 框架层(Pydantic) | 把 if not x: return 400 删掉,改类型标注 |
| 错误响应 | 自定义 JSON | 422 detail 数组 |
前端要能解析两种结构 |
| 文档 | 扩展 | 内建 | 可直接删掉手写文档 |
| 异步 | 有限 | 原生 | handler 是否 async def 要逐个判断 |
| 依赖注入 | 手动 | Depends |
可收敛"获取 db/session"的样板 |
2. 同一接口的两种写法
2.1 Flask
@app.route("/items/<int:item_id>")
def item(item_id):
if item_id > 100:
abort(404)
return jsonify(id=item_id, name="apple")
2.2 FastAPI
@app.get("/items/{item_id}")
def get_item(item_id: int):
if item_id not in DB:
raise HTTPException(status_code=404, detail="item not found")
return {"id": item_id, **DB[item_id].model_dump()}
代码量接近,但"校验"的归属完全不同:Flask 里你必须自己判断 item_id 是否合法;FastAPI 在进入函数前已经校验完。
3. 实测:错误语义差异(迁移时最容易翻车)
| 请求 | Flask 3.1.3 | FastAPI 0.162.0 |
|---|---|---|
/items/abc |
404 | 422 |
/items/999(不存在) |
404(业务 abort) |
404(HTTPException) |
| 请求体字段非法 | 手写决定(常见 400) | 422 + 逐字段明细 |
为什么这条必须写进迁移清单:
| 客户端预期 | Flask 行为 | FastAPI 行为 |
|---|---|---|
| "接口不存在" | 看到 404 ✅ | 看到 422(需先看 detail) |
| "参数传错" | 看到 404 ❌(误判为接口没了) | 看到 422 ✅ |
迁移动作:前端/SDK 需要同时兼容两种错误结构,或服务端加一层"统一错误包装"(把 422 转成业务错误码)。
4. 迁移清单(可执行)
| # | 项 | 动作 |
|---|---|---|
| 1 | 路由 | @app.route(..., methods=["POST"]) → @app.post(...) |
| 2 | 参数 | 手写校验 → 类型标注 + Pydantic 模型 |
| 3 | 错误 | abort/jsonify → HTTPException + 自定义 exception_handler |
| 4 | 依赖 | 手动获取 → Depends(db、鉴权、分页) |
| 5 | 异步 | 逐个判断:有阻塞 IO 的 handler 改 async 或丢线程池 |
| 6 | 文档 | 删手写 Swagger 配置,使用 /docs |
| 7 | 测试 | 断言状态码的地方要改(404 → 422) |
| 8 | 部署 | flask run → uvicorn(或 gunicorn + uvicorn worker) |
5. 异步这件事(E-S10-011)
| 写法 | 行为 |
|---|---|
def handler()(同步) |
FastAPI 放线程池执行;阻塞调用会占线程 |
async def handler() |
事件循环执行;内部若有阻塞调用会卡住整个循环 |
判据:
handler 里有没有阻塞调用(同步 DB 驱动、requests、文件 IO)?
├── 有,且用同步库 → 用 def(交给线程池)
└── 有,但已换成异步库(asyncpg/httpx) → 用 async def
最常见的错误:async def 里调 requests.get() —— 单请求看起来没事,并发一上来整个服务卡死。
6. 五个误区
| 误区 | 真相 |
|---|---|
| "FastAPI 一定更快" | 高并发+异步 IO 才明显;纯计算型差别不大 |
| "迁移就是改装饰器" | 校验/错误/测试/部署都要改 |
"async def 越多越好" |
阻塞调用放进 async 会拖垮事件循环 |
| "Flask 不能异步" | 3.x 支持,但生态多为同步 |
| "错误码可以最后再调" | 前端会先炸;状态码语义要在迁移前定 |
7. 自检
/items/abc在两框架下的状态码分别是什么?为什么?- 迁移清单里哪一项最容易漏?
async def里调同步requests会发生什么?- 为什么建议加"统一错误包装层"?
- 什么情况下迁移反而不划算?
8. 下一篇
S10-A01 · Web 框架选型 ADR(FastAPI / Flask / Django)
对照完之后,进入架构决策:按什么维度选、被否方案怎么留证据、以及"什么时候该选 Django"。
本篇属于系列《S10 · Python 语言与环境》。扩展点 E-S10-011/012。两框架错误语义差异为 2026-11-05 实测。
S10-J02 · Flask ↔ FastAPI:迁移与对照
https://www.clxhxhhr.top/posts/5322/ S10-J01 · 依赖治理:requirements / environment.yml / 锁版本
S10-A01 · Web 框架选型 ADR(FastAPI / Flask / Django)
评论
0 条
还没有评论,先写一条吧。