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

13 KiB
Raw Blame History

Flask 到 FastAPI 迁移指南

📋 目录

  1. 迁移概览
  2. 核心概念对比
  3. 代码迁移示例
  4. 项目迁移分析
  5. 常见迁移问题

迁移概览

为什么迁移到 FastAPI

  1. 性能提升FastAPI 性能接近 NodeJS 和 Go
  2. 自动文档:自动生成 API 文档
  3. 类型安全:基于 Python 类型提示
  4. 异步支持:原生支持 async/await
  5. 现代特性:符合现代 Python 开发标准

迁移步骤

  1. 安装 FastAPIpip install fastapi uvicorn
  2. 替换应用实例Flask()FastAPI()
  3. 更新路由装饰器:基本语法相同
  4. 处理异步:添加 async/await
  5. 数据验证:使用 Pydantic 模型
  6. 更新响应:使用 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.configapp.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()
  • defasync 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_requestafter_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.configapp.state
  • 更新生命周期事件(before_first_requeston_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.mdFASTAPI_QUICK_REFERENCE.md 一起学习效果更好。