Files
jdy_fastapi/FLASK_TO_FASTAPI_MIGRATION.md
T
2025-11-07 17:48:49 +08:00

621 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Flask 到 FastAPI 迁移指南
## 📋 目录
1. [迁移概览](#迁移概览)
2. [核心概念对比](#核心概念对比)
3. [代码迁移示例](#代码迁移示例)
4. [项目迁移分析](#项目迁移分析)
5. [常见迁移问题](#常见迁移问题)
---
## 迁移概览
### 为什么迁移到 FastAPI
1. **性能提升**FastAPI 性能接近 NodeJS 和 Go
2. **自动文档**:自动生成 API 文档
3. **类型安全**:基于 Python 类型提示
4. **异步支持**:原生支持 async/await
5. **现代特性**:符合现代 Python 开发标准
### 迁移步骤
1.**安装 FastAPI**`pip install fastapi uvicorn`
2.**替换应用实例**`Flask()``FastAPI()`
3.**更新路由装饰器**:基本语法相同
4.**处理异步**:添加 `async/await`
5.**数据验证**:使用 Pydantic 模型
6.**更新响应**:使用 FastAPI 响应类
---
## 核心概念对比
### 1. 应用实例
#### Flask
```python
from flask import Flask
app = Flask(__name__)
```
#### FastAPI
```python
from fastapi import FastAPI
app = FastAPI()
```
### 2. 路由定义
#### Flask
```python
@app.route('/items', methods=['GET'])
def get_items():
return jsonify({'items': []})
```
#### FastAPI
```python
@app.get('/items')
async def get_items():
return {'items': []}
```
**区别**
- FastAPI 使用 `@app.get()` 而不是 `@app.route(methods=['GET'])`
- FastAPI 函数通常是 `async`
- FastAPI 直接返回字典,自动转换为 JSON
### 3. 获取请求数据
#### Flask
```python
from flask import request
@app.route('/items', methods=['POST'])
def create_item():
data = request.get_json()
name = data.get('name')
return jsonify({'name': name})
```
#### FastAPI
```python
from fastapi import Request
from pydantic import BaseModel
class Item(BaseModel):
name: str
@app.post('/items')
async def create_item(item: Item):
return {'name': item.name}
```
**区别**
- FastAPI 使用 Pydantic 模型自动验证
- 不需要手动获取 JSON 数据
- 类型自动验证和转换
### 4. 路径参数
#### Flask
```python
@app.route('/items/<int:item_id>')
def get_item(item_id):
return jsonify({'item_id': item_id})
```
#### FastAPI
```python
@app.get('/items/{item_id}')
async def get_item(item_id: int):
return {'item_id': item_id}
```
**区别**
- FastAPI 使用 `{item_id}` 而不是 `<int:item_id>`
- 类型在函数参数中指定
### 5. 查询参数
#### Flask
```python
from flask import request
@app.route('/items')
def get_items():
skip = request.args.get('skip', 0, type=int)
limit = request.args.get('limit', 10, type=int)
return jsonify({'skip': skip, 'limit': limit})
```
#### FastAPI
```python
@app.get('/items')
async def get_items(skip: int = 0, limit: int = 10):
return {'skip': skip, 'limit': limit}
```
**区别**
- FastAPI 查询参数直接在函数参数中定义
- 默认值自动处理
- 类型自动验证
### 6. 请求头
#### Flask
```python
from flask import request
@app.route('/items')
def get_items():
user_agent = request.headers.get('User-Agent')
return jsonify({'user_agent': user_agent})
```
#### FastAPI
```python
from fastapi import Request, Header
@app.get('/items')
async def get_items(request: Request):
user_agent = request.headers.get('user-agent')
return {'user_agent': user_agent}
# 或者使用 Header
@app.get('/items')
async def get_items(user_agent: str = Header(None)):
return {'user_agent': user_agent}
```
### 7. 响应
#### Flask
```python
from flask import jsonify, Response
@app.route('/items')
def get_items():
return jsonify({'items': []})
# 或
return Response('text', mimetype='text/plain')
```
#### FastAPI
```python
from fastapi.responses import JSONResponse, PlainTextResponse
@app.get('/items')
async def get_items():
return {'items': []} # 自动转换为 JSON
# 或
return JSONResponse({'items': []})
# 或
return PlainTextResponse('text')
```
### 8. 错误处理
#### Flask
```python
from flask import abort
@app.route('/items/<int:item_id>')
def get_item(item_id):
if item_id not in items:
abort(404, description="Item not found")
return jsonify({'item_id': item_id})
```
#### FastAPI
```python
from fastapi import HTTPException
@app.get('/items/{item_id}')
async def get_item(item_id: int):
if item_id not in items:
raise HTTPException(status_code=404, detail="Item not found")
return {'item_id': item_id}
```
### 9. 应用状态
#### Flask
```python
from flask import g
@app.before_request
def before_request():
g.db = get_database()
@app.route('/items')
def get_items():
db = g.db
return jsonify({'items': []})
```
#### FastAPI
```python
@app.on_event("startup")
def startup():
app.state.db = get_database()
@app.get('/items')
async def get_items():
db = app.state.db
return {'items': []}
```
### 10. 中间件
#### Flask
```python
@app.before_request
def before_request():
# 请求前处理
pass
@app.after_request
def after_request(response):
# 请求后处理
response.headers['X-Custom'] = 'value'
return response
```
#### FastAPI
```python
@app.middleware("http")
async def add_custom_header(request: Request, call_next):
# 请求前处理
response = await call_next(request)
# 请求后处理
response.headers['X-Custom'] = 'value'
return response
```
---
## 代码迁移示例
### 示例 1:简单的 API 端点
#### Flask 版本
```python
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/api/users', methods=['POST'])
def create_user():
data = request.get_json()
if not data or 'name' not in data:
return jsonify({'error': '缺少 name 字段'}), 400
name = data['name']
age = data.get('age', 0)
return jsonify({'name': name, 'age': age}), 201
```
#### FastAPI 版本
```python
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class User(BaseModel):
name: str
age: int = 0
@app.post('/api/users', status_code=201)
async def create_user(user: User):
return {'name': user.name, 'age': user.age}
```
**改进点**
- ✅ 自动数据验证
- ✅ 类型安全
- ✅ 更简洁的代码
- ✅ 自动生成 API 文档
### 示例 2:带路径参数的端点
#### Flask 版本
```python
@app.route('/api/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
if user_id not in users:
return jsonify({'error': '用户不存在'}), 404
return jsonify(users[user_id])
```
#### FastAPI 版本
```python
@app.get('/api/users/{user_id}')
async def get_user(user_id: int):
if user_id not in users:
raise HTTPException(status_code=404, detail='用户不存在')
return users[user_id]
```
### 示例 3:文件上传
#### Flask 版本
```python
from flask import request
@app.route('/upload', methods=['POST'])
def upload_file():
if 'file' not in request.files:
return jsonify({'error': '没有文件'}), 400
file = request.files['file']
# 处理文件
return jsonify({'filename': file.filename})
```
#### FastAPI 版本
```python
from fastapi import UploadFile, File
@app.post('/upload')
async def upload_file(file: UploadFile = File(...)):
# 处理文件
return {'filename': file.filename}
```
---
## 项目迁移分析
### 当前项目的迁移情况
#### 1. 应用初始化(main.py
**Flask 版本(推测)**
```python
from flask import Flask
app = Flask(__name__)
@app.before_first_request
def initialize():
app.config['app_tools'] = AppTools(Config)
# ...
```
**FastAPI 版本(当前)**
```python
from fastapi import FastAPI
app = FastAPI(title="简道云FastAPI服务")
@app.on_event("startup")
def on_startup():
app.state.app_tools = AppTools(Config)
app.state.logger = setup_global_logger(Config)
# ...
```
**迁移要点**
-`Flask()``FastAPI()`
-`@app.before_first_request``@app.on_event("startup")`
-`app.config``app.state`
#### 2. 路由处理(main.py
**Flask 版本(推测)**
```python
@app.route('/webhook', methods=['POST'])
def webhook():
data = request.get_json()
header = request.headers
# 处理逻辑
return jsonify(result)
```
**FastAPI 版本(当前)**
```python
@app.post("/webhook")
async def webhook(request: Request):
data = await request.json()
header = request.headers
# 处理逻辑
return JSONResponse(result)
```
**迁移要点**
-`@app.route(methods=['POST'])``@app.post()`
-`def``async def`
-`request.get_json()``await request.json()`
-`jsonify()``JSONResponse()` 或直接返回字典
#### 3. 任务队列处理
**项目特点**
- 使用自定义任务队列(`Queue` + `threading`
- 在异步函数中调用同步函数
**FastAPI 处理方式**
```python
# 在线程池中执行同步函数
result = await anyio.to_thread.run_sync(response_queue.get)
```
**迁移要点**
- ✅ 使用 `anyio.to_thread.run_sync()` 在线程池中执行同步代码
- ✅ 保持原有的任务队列机制
---
## 常见迁移问题
### Q1: 如何处理 Flask 的 `g` 对象?
**A:** 使用 `app.state` 或依赖注入:
```python
# Flask
from flask import g
g.db = get_db()
# FastAPI 方式 1:使用 app.state
app.state.db = get_db()
db = app.state.db
# FastAPI 方式 2:使用依赖注入(推荐)
def get_db():
db = "database"
yield db
@app.get("/items")
async def get_items(db: str = Depends(get_db)):
return {"db": db}
```
### Q2: 如何处理 Flask 的 `session`
**A:** FastAPI 不内置 session,需要手动实现或使用第三方库:
```python
from fastapi import FastAPI, Request
from starlette.middleware.sessions import SessionMiddleware
app = FastAPI()
app.add_middleware(SessionMiddleware, secret_key="secret")
@app.post("/login")
async def login(request: Request):
request.session['user'] = 'username'
return {"message": "Logged in"}
```
### Q3: 如何处理 Flask 的 `url_for`
**A:** 使用 `Request` 对象构建 URL
```python
from fastapi import Request
@app.get("/items/{item_id}")
async def get_item(item_id: int, request: Request):
url = str(request.url_for('get_item', item_id=item_id))
return {"url": url}
```
### Q4: 如何迁移 Flask 的蓝图(Blueprint)?
**A:** 使用 FastAPI 的 `APIRouter`
```python
from fastapi import APIRouter
router = APIRouter()
@router.get("/items")
async def get_items():
return {"items": []}
# 在主应用中注册
app.include_router(router, prefix="/api")
```
### Q5: 如何处理 Flask 的 `current_app`
**A:** 使用依赖注入或直接访问 `app`
```python
# Flask
from flask import current_app
config = current_app.config
# FastAPI
from fastapi import Request
@app.get("/config")
async def get_config(request: Request):
app = request.app
# 访问应用配置
return {"config": "value"}
```
### Q6: 如何迁移 Flask 的 `before_request` 和 `after_request`
**A:** 使用中间件:
```python
@app.middleware("http")
async def middleware(request: Request, call_next):
# before_request 逻辑
print("请求前")
response = await call_next(request)
# after_request 逻辑
print("请求后")
response.headers["X-Custom"] = "value"
return response
```
---
## 迁移检查清单
### 基础迁移
- [ ] 替换 `Flask()``FastAPI()`
- [ ] 更新路由装饰器(`@app.route()``@app.get()` 等)
- [ ] 添加 `async` 关键字
- [ ] 更新请求数据获取方式
- [ ] 更新响应返回方式
### 高级迁移
- [ ] 使用 Pydantic 模型进行数据验证
- [ ] 迁移应用状态(`app.config``app.state`
- [ ] 更新生命周期事件(`before_first_request``on_event("startup")`
- [ ] 迁移中间件
- [ ] 更新错误处理
### 测试和优化
- [ ] 测试所有 API 端点
- [ ] 验证数据验证功能
- [ ] 检查性能提升
- [ ] 更新文档
- [ ] 更新依赖项
---
## 总结
### 迁移优势
1. **性能提升**FastAPI 性能显著优于 Flask
2. **开发效率**:自动文档、自动验证减少开发时间
3. **类型安全**:类型提示提供更好的 IDE 支持
4. **现代特性**:异步支持、依赖注入等现代特性
### 注意事项
1. **异步编程**:需要理解 `async/await`
2. **Pydantic**:需要学习 Pydantic 模型定义
3. **依赖注入**:FastAPI 的依赖注入系统需要适应
4. **生态系统**:Flask 的某些扩展可能需要替代方案
### 建议
1. **逐步迁移**:不要一次性迁移所有代码
2. **保持兼容**:在迁移过程中保持 API 兼容性
3. **充分测试**:迁移后充分测试所有功能
4. **学习资源**:参考 FastAPI 官方文档和示例
---
**提示**:这个迁移指南帮助你理解从 Flask 到 FastAPI 的迁移过程。结合 `FASTAPI_LEARNING.md``FASTAPI_QUICK_REFERENCE.md` 一起学习效果更好。