13 KiB
13 KiB
Flask 到 FastAPI 迁移指南
📋 目录
迁移概览
为什么迁移到 FastAPI?
- 性能提升:FastAPI 性能接近 NodeJS 和 Go
- 自动文档:自动生成 API 文档
- 类型安全:基于 Python 类型提示
- 异步支持:原生支持 async/await
- 现代特性:符合现代 Python 开发标准
迁移步骤
- ✅ 安装 FastAPI:
pip install fastapi uvicorn - ✅ 替换应用实例:
Flask()→FastAPI() - ✅ 更新路由装饰器:基本语法相同
- ✅ 处理异步:添加
async/await - ✅ 数据验证:使用 Pydantic 模型
- ✅ 更新响应:使用 FastAPI 响应类
核心概念对比
1. 应用实例
Flask
from flask import Flask
app = Flask(__name__)
FastAPI
from fastapi import FastAPI
app = FastAPI()
2. 路由定义
Flask
@app.route('/items', methods=['GET'])
def get_items():
return jsonify({'items': []})
FastAPI
@app.get('/items')
async def get_items():
return {'items': []}
区别:
- FastAPI 使用
@app.get()而不是@app.route(methods=['GET']) - FastAPI 函数通常是
async - FastAPI 直接返回字典,自动转换为 JSON
3. 获取请求数据
Flask
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
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
@app.route('/items/<int:item_id>')
def get_item(item_id):
return jsonify({'item_id': item_id})
FastAPI
@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
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
@app.get('/items')
async def get_items(skip: int = 0, limit: int = 10):
return {'skip': skip, 'limit': limit}
区别:
- FastAPI 查询参数直接在函数参数中定义
- 默认值自动处理
- 类型自动验证
6. 请求头
Flask
from flask import request
@app.route('/items')
def get_items():
user_agent = request.headers.get('User-Agent')
return jsonify({'user_agent': user_agent})
FastAPI
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
from flask import jsonify, Response
@app.route('/items')
def get_items():
return jsonify({'items': []})
# 或
return Response('text', mimetype='text/plain')
FastAPI
from fastapi.responses import JSONResponse, PlainTextResponse
@app.get('/items')
async def get_items():
return {'items': []} # 自动转换为 JSON
# 或
return JSONResponse({'items': []})
# 或
return PlainTextResponse('text')
8. 错误处理
Flask
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
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
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
@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
@app.before_request
def before_request():
# 请求前处理
pass
@app.after_request
def after_request(response):
# 请求后处理
response.headers['X-Custom'] = 'value'
return response
FastAPI
@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 版本
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 版本
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 版本
@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 版本
@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 版本
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 版本
from fastapi import UploadFile, File
@app.post('/upload')
async def upload_file(file: UploadFile = File(...)):
# 处理文件
return {'filename': file.filename}
项目迁移分析
当前项目的迁移情况
1. 应用初始化(main.py)
Flask 版本(推测):
from flask import Flask
app = Flask(__name__)
@app.before_first_request
def initialize():
app.config['app_tools'] = AppTools(Config)
# ...
FastAPI 版本(当前):
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 版本(推测):
@app.route('/webhook', methods=['POST'])
def webhook():
data = request.get_json()
header = request.headers
# 处理逻辑
return jsonify(result)
FastAPI 版本(当前):
@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 处理方式:
# 在线程池中执行同步函数
result = await anyio.to_thread.run_sync(response_queue.get)
迁移要点:
- ✅ 使用
anyio.to_thread.run_sync()在线程池中执行同步代码 - ✅ 保持原有的任务队列机制
常见迁移问题
Q1: 如何处理 Flask 的 g 对象?
A: 使用 app.state 或依赖注入:
# 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,需要手动实现或使用第三方库:
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:
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:
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:
# 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: 使用中间件:
@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 端点
- 验证数据验证功能
- 检查性能提升
- 更新文档
- 更新依赖项
总结
迁移优势
- 性能提升:FastAPI 性能显著优于 Flask
- 开发效率:自动文档、自动验证减少开发时间
- 类型安全:类型提示提供更好的 IDE 支持
- 现代特性:异步支持、依赖注入等现代特性
注意事项
- 异步编程:需要理解
async/await - Pydantic:需要学习 Pydantic 模型定义
- 依赖注入:FastAPI 的依赖注入系统需要适应
- 生态系统:Flask 的某些扩展可能需要替代方案
建议
- 逐步迁移:不要一次性迁移所有代码
- 保持兼容:在迁移过程中保持 API 兼容性
- 充分测试:迁移后充分测试所有功能
- 学习资源:参考 FastAPI 官方文档和示例
提示:这个迁移指南帮助你理解从 Flask 到 FastAPI 的迁移过程。结合 FASTAPI_LEARNING.md 和 FASTAPI_QUICK_REFERENCE.md 一起学习效果更好。