Flask vs FastAPI 全方位对比与实战

一、引言

在 Python Web 开发领域,Flask 和 FastAPI 是目前最受欢迎的两款轻量级框架。Flask 诞生于 2010 年,凭借其极简设计理念和庞大的插件生态,成为 Python 开发者构建 Web 应用的入门首选,至今仍被 Netflix、Reddit、Lyft 等知名公司用于处理海量生产请求。FastAPI 则于 2018 年横空出世,凭借原生异步支持、自动文档生成和类型安全三大核心特性,迅速成长为 Python 生态中增长最快的 Web 框架,GitHub 星标数已超过 80k。

本文将从框架特性、性能表现、开发体验等多个维度对两者进行深度对比,并提供完整的实战教程,帮助开发者根据项目需求做出合理的技术选型。

二、Flask 框架详细介绍

2.1 框架概述

Flask 是一个基于 Python 的轻量级 Web 框架,采用 WSGI(Web Server Gateway Interface) 规范设计。Flask 的核心极简主义设计理念使其成为 Python 生态中最灵活的 Web 框架之一------它只提供路由、请求处理和模板渲染等最基础的功能,不强制使用特定的项目结构或依赖,将数据库集成、表单验证等功能留给开发者自由选择。

Flask 依赖三个核心组件:

  • Werkzeug:WSGI 工具包,处理底层 HTTP 路由和请求响应
  • Jinja2:强大的模板引擎,用于渲染 HTML 页面
  • Click:命令行界面工具包,用于管理 CLI 命令

2.2 2026 年 Flask 生态现状

截至 2026 年,Flask 稳定版本为 3.1.x,要求 Python 3.10 或更高版本。Flask 3.x 系列在多个方面进行了重要升级:

  • 原生异步支持优化:3.x 版本原生优化了异步视图函数与中间件,能更高效地处理 I/O 密集型任务,虽然仍是 WSGI 框架,但异步处理能力已有显著提升。
  • 类型提示增强:全面拥抱现代 Python 特性,在核心 API 层面增加了完整的类型注解支持。
  • 安全更新:如 Flask 3.1.3 修复了安全漏洞,CSRF 保护等安全特性持续增强。

Flask 拥有一个庞大且成熟的扩展生态系统,核心扩展包括:

  • Flask-SQLAlchemy:数据库 ORM 操作
  • Flask-Migrate:数据库迁移管理
  • Flask-JWT-Extended:基于 JWT 的身份认证
  • Flask-RESTful:专门用于构建 REST API 的扩展
  • Flask-WTF:表单验证与 CSRF 保护

2.3 核心特性分析

Flask 的设计哲学是"简洁但不简单"。其核心优势体现在以下几个方面:

(1)极低的学习曲线

一个基本的 Flask 应用只需 5 行代码即可运行,对于刚刚接触 Web 开发的 Python 开发者来说几乎没有入门门槛。

(2)装饰器路由系统

Flask 采用直观的装饰器设计,让 URL 与 Python 函数的对应关系一目了然,代码可读性极高。

(3)灵活的扩展机制

作为"微框架",Flask 不提供开箱即用的数据库 ORM 或表单验证功能,但通过数百个社区维护的扩展,开发者可以根据需求自由组合所需组件。

(4)成熟的测试和生产实践

Flask 已在生产环境中运行十余年,拥有无数经过实战验证的最佳实践和专业调试工具(如 Flask-DebugToolbar),让开发和调试过程十分流畅。

三、FastAPI 框架详细介绍

3.1 框架概述

FastAPI 是一个基于 ASGI(Asynchronous Server Gateway Interface) 标准的现代 Web 框架,专门为 API 开发而设计。它建立在两大核心组件之上:

  • Starlette:轻量级 ASGI 框架,负责 Web 部分的基础设施
  • Pydantic:Python 数据验证库,基于类型提示进行高效数据校验

FastAPI 于 2018 年开源发布,凭借三大杀手级特性改变了 Python API 开发范式:自动交互式 API 文档原生异步支持类型安全的数据验证

3.2 核心特性分析

(1)性能

FastAPI 是目前最快的 Python Web 框架之一,性能可与 NodeJS 和 Go 比肩。在 TechEmpower 基准测试中,FastAPI 在 JSON 序列化场景下的吞吐量可达 Flask 的 2-3 倍,在某些优化场景下甚至能达到 6 倍的性能差距。

(2)自动数据验证

这是 FastAPI 最强大的优势。通过 Pydantic 模型配合 Python 类型提示,FastAPI 可以自动解析和验证请求体、查询参数、路径参数等数据,无效请求会自动返回详细的 422 错误信息,大幅减少样板代码。

(3)原生异步支持

FastAPI 基于 ASGI 标准,原生支持 async/await 语法。对于需要调用多个外部 API、数据库查询等 I/O 密集型任务,异步并发可以将性能提升 3-5 倍,而不会阻塞事件循环。

(4)自动 API 文档

只需编写代码,FastAPI 就会基于 OpenAPI 规范自动生成交互式 API 文档。访问 /docs 即可使用 Swagger UI 进行交互测试,访问 /redoc 则可获得 ReDoc 风格的文档界面。

3.3 最新演进(截至 2026 年)

FastAPI 社区保持高速迭代。最新版本新增了多项重要特性:

  • Pydantic Rust 序列化优化:当使用 Pydantic 返回类型或响应模型时,JSON 响应性能提升 2 倍以上,通过 Rust 层面的优化实现更快的数据序列化。
  • Streaming JSON Lines 支持:通过 yield 语法支持流式传输 JSON Lines 和二进制数据,适用于大数据量和实时数据流场景。
  • Starlette 版本升级:从 0.40.0+ 升级至 0.46.0+,提供了更好的异常处理和稳定性。

四、Flask vs FastAPI 核心对比

4.1 核心指标对比表

对比维度 Flask FastAPI
诞生年份 2010 年 2018 年
GitHub Stars ~69k+ ~83k+
底层协议 WSGI(同步) ASGI(原生异步)
类型提示 可选 强制(Pydantic)
数据验证 手动 自动(Pydantic)
API 文档 需第三方扩展 自动生成(Swagger/ReDoc)
异步处理 3.x 版本部分支持 原生 async/await
学习曲线
生态系统 成熟庞大 快速增长
适用场景 简单 Web 应用、原型开发、传统网站 高性能 API、微服务、实时应用

4.2 性能深度对比

性能是 Flask 和 FastAPI 之间最显著的差异。

Flask 基于 WSGI 协议,采用同步阻塞模型。在处理请求时,每个工作线程在等待 I/O 操作(如数据库查询、HTTP 调用)完成期间会被占用,无法处理其他请求。在高并发场景下,这种模型会成为性能瓶颈。

FastAPI 基于 ASGI 协议,原生支持异步编程。当一个请求在等待 I/O 时,事件循环可以切换到处理其他请求,实现了高效的并发处理。

根据 2025 年的性能测试数据,两者差距显著:

性能指标 Flask FastAPI
吞吐量(优化后) ~8,742 请求/秒 ~52,348 请求/秒
平均延迟 ~18 ms ~8 ms
内存占用 ~120 MB ~65 MB

Flask 通过 Gunicorn + gevent 优化后可提升并发处理能力,但在长连接场景下仍显不足。

4.3 代码对比:Hello World

为了直观感受两个框架的差异,以下是最简单应用的代码对比:

Flask 版本:

python 复制代码
from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
    return {'message': 'Hello, World!'}

if __name__ == '__main__':
    app.run(debug=True)

FastAPI 版本:

python 复制代码
from fastapi import FastAPI

app = FastAPI()

@app.get('/')
def hello():
    return {'message': 'Hello, World!'}

# 使用以下命令运行:uvicorn main:app --reload

两者在最小化应用上都很简洁,差异在于 FastAPI 使用 @app.get() 这类 HTTP 方法装饰器,而 Flask 统一使用 @app.route() 并通过 methods= 参数指定 HTTP 方法。

4.4 数据验证代码对比

在实际 API 开发中,数据验证的差异最能体现两者的设计哲学:

Flask 手动验证:

python 复制代码
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/users', methods=['POST'])
def create_user():
    data = request.get_json()
    
    # 手动验证------需要大量样板代码
    if not data or 'name' not in data:
        return jsonify({'error': 'name is required'}), 400
    if not isinstance(data.get('age'), int) or data['age'] < 0:
        return jsonify({'error': 'age must be a positive integer'}), 400
    if 'email' not in data or '@' not in data['email']:
        return jsonify({'error': 'valid email is required'}), 400
    
    return jsonify({'id': 1, **data}), 201

FastAPI 自动验证:

python 复制代码
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()

class UserCreate(BaseModel):
    name: str
    age: int
    email: EmailStr

@app.post('/users')
def create_user(user: UserCreate):
    # 验证在函数运行之前自动完成
    # 无效请求会自动返回详细的 422 错误响应
    return {'id': 1, **user.model_dump()}

FastAPI 利用 Python 类型提示和 Pydantic 模型自动验证所有传入数据,无需任何手动检查,错误响应会详细说明哪个字段因何原因验证失败。

4.5 异步处理代码对比

FastAPI 原生异步(推荐方式):

python 复制代码
from fastapi import FastAPI
import httpx

app = FastAPI()

@app.get('/data')
async def get_data():
    async with httpx.AsyncClient() as client:
        # 等待时其他请求可以被处理,不阻塞
        response = await client.get('https://api.example.com/data')
        return response.json()

Flask 同步方式(传统)或异步支持:

python 复制代码
from flask import Flask
import requests

app = Flask(__name__)

@app.route('/data')
def get_data():
    # 阻塞调用------整个线程在等待响应期间被占用
    response = requests.get('https://api.example.com/data')
    return response.json()

FastAPI 原生处理异步,非常适合 API 调用和数据库查询等 I/O 密集型任务,而 Flask 在此类并发场景中需要借助额外的工具如 Gunicorn + gevent 才能部分模拟异步效果。

五、Flask 完整开发教程

本节将带领读者从零开始,使用 Flask 构建一个完整的用户管理 RESTful API。

5.1 环境搭建

前置要求:Python 3.10 或更高版本,Flask 3.x 充分利用了现代 Python 的类型提示和异步特性。

bash 复制代码
# 创建项目目录
mkdir flask-user-api && cd flask-user-api

# 创建虚拟环境
python3 -m venv venv
source venv/bin/activate        # Linux/Mac
# venv\Scripts\activate         # Windows

# 安装 Flask 及常用扩展
pip install Flask
pip install Flask-SQLAlchemy    # 数据库 ORM
pip install Flask-Migrate        # 数据库迁移
pip install Flask-JWT-Extended   # JWT 身份验证
pip install python-dotenv        # 环境变量管理
pip freeze > requirements.txt

5.2 项目结构设计

采用模块化的应用工厂模式,使项目易于扩展和维护:

复制代码
flask-user-api/
├── app/
│   ├── __init__.py          # 应用工厂
│   ├── models.py            # 数据库模型
│   ├── routes/
│   │   ├── __init__.py
│   │   └── users.py         # 用户路由
│   ├── utils/
│   │   ├── __init__.py
│   │   └── auth.py          # 认证工具
│   └── config.py            # 配置管理
├── migrations/              # 数据库迁移文件(自动生成)
├── run.py                   # 应用入口
├── requirements.txt
└── .env                     # 环境变量

5.3 配置文件

创建 app/config.py

python 复制代码
import os
from dotenv import load_dotenv

load_dotenv()

class Config:
    SECRET_KEY = os.getenv('SECRET_KEY', 'dev-secret-key')
    SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL', 'sqlite:///app.db')
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    JWT_SECRET_KEY = os.getenv('JWT_SECRET_KEY', 'jwt-dev-secret')
    
class DevelopmentConfig(Config):
    DEBUG = True
    
class ProductionConfig(Config):
    DEBUG = False

5.4 创建应用工厂

app/__init__.py

python 复制代码
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_jwt_extended import JWTManager
from .config import Config

db = SQLAlchemy()
migrate = Migrate()
jwt = JWTManager()

def create_app(config_class=Config):
    app = Flask(__name__)
    app.config.from_object(config_class)
    
    # 初始化扩展
    db.init_app(app)
    migrate.init_app(app, db)
    jwt.init_app(app)
    
    # 注册蓝图
    from .routes.users import users_bp
    app.register_blueprint(users_bp, url_prefix='/api/users')
    
    @app.route('/health')
    def health_check():
        return {'status': 'ok'}
    
    return app

5.5 定义数据模型

app/models.py

python 复制代码
from . import db
from datetime import datetime

class User(db.Model):
    __tablename__ = 'users'
    
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password_hash = db.Column(db.String(256), nullable=False)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
    
    def to_dict(self):
        return {
            'id': self.id,
            'username': self.username,
            'email': self.email,
            'created_at': self.created_at.isoformat() if self.created_at else None
        }

5.6 实现用户 API 路由

app/routes/users.py

python 复制代码
from flask import Blueprint, request, jsonify
from werkzeug.security import generate_password_hash, check_password_hash
from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity
from ..models import User
from .. import db

users_bp = Blueprint('users', __name__)

# 用户注册
@users_bp.route('/register', methods=['POST'])
def register():
    data = request.get_json()
    
    # 手动验证输入
    if not data or not data.get('username') or not data.get('email') or not data.get('password'):
        return jsonify({'error': 'Missing required fields'}), 400
    
    # 检查用户是否已存在
    if User.query.filter_by(username=data['username']).first():
        return jsonify({'error': 'Username already exists'}), 409
    
    if User.query.filter_by(email=data['email']).first():
        return jsonify({'error': 'Email already exists'}), 409
    
    user = User(
        username=data['username'],
        email=data['email'],
        password_hash=generate_password_hash(data['password'])
    )
    
    db.session.add(user)
    db.session.commit()
    
    access_token = create_access_token(identity=user.id)
    return jsonify({
        'message': 'User created successfully',
        'user': user.to_dict(),
        'access_token': access_token
    }), 201

# 用户登录
@users_bp.route('/login', methods=['POST'])
def login():
    data = request.get_json()
    
    if not data or not data.get('username') or not data.get('password'):
        return jsonify({'error': 'Username and password required'}), 400
    
    user = User.query.filter_by(username=data['username']).first()
    
    if not user or not check_password_hash(user.password_hash, data['password']):
        return jsonify({'error': 'Invalid credentials'}), 401
    
    access_token = create_access_token(identity=user.id)
    return jsonify({
        'message': 'Login successful',
        'access_token': access_token,
        'user': user.to_dict()
    })

# 获取当前用户信息(需要认证)
@users_bp.route('/me', methods=['GET'])
@jwt_required()
def get_current_user():
    user_id = get_jwt_identity()
    user = User.query.get(user_id)
    
    if not user:
        return jsonify({'error': 'User not found'}), 404
    
    return jsonify(user.to_dict())

# 更新用户信息
@users_bp.route('/me', methods=['PUT'])
@jwt_required()
def update_user():
    user_id = get_jwt_identity()
    user = User.query.get(user_id)
    
    if not user:
        return jsonify({'error': 'User not found'}), 404
    
    data = request.get_json()
    
    if 'username' in data:
        user.username = data['username']
    if 'email' in data:
        user.email = data['email']
    if 'password' in data:
        user.password_hash = generate_password_hash(data['password'])
    
    db.session.commit()
    
    return jsonify({
        'message': 'User updated successfully',
        'user': user.to_dict()
    })

# 删除用户
@users_bp.route('/me', methods=['DELETE'])
@jwt_required()
def delete_user():
    user_id = get_jwt_identity()
    user = User.query.get(user_id)
    
    if not user:
        return jsonify({'error': 'User not found'}), 404
    
    db.session.delete(user)
    db.session.commit()
    
    return jsonify({'message': 'User deleted successfully'})

5.7 应用入口

run.py

python 复制代码
from app import create_app

app = create_app()

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=5000)

5.8 运行与测试

bash 复制代码
# 初始化数据库
flask db init
flask db migrate -m "Initial migration"
flask db upgrade

# 运行应用
python run.py

启动后访问 http://localhost:5000/health 验证服务状态。可以使用 Postman、curl 或任何 REST 客户端测试各个 API 端点。

六、FastAPI 完整开发教程

本节将使用 FastAPI 构建与 Flask 版本功能相同的用户管理 API,展现 FastAPI 现代化开发风格。

6.1 环境搭建

FastAPI 要求 Python 3.8+(推荐 3.10 或更高版本):

bash 复制代码
# 创建项目目录
mkdir fastapi-user-api && cd fastapi-user-api

# 创建虚拟环境
python3 -m venv venv
source venv/bin/activate

# 安装 FastAPI 及核心依赖
pip install fastapi uvicorn[standard]
pip install sqlalchemy asyncpg            # 异步数据库驱动
pip install databases                      # 异步数据库工具包
pip install python-jose[cryptography]     # JWT 处理
pip install passlib[bcrypt]               # 密码哈希
pip install pydantic-settings             # 配置管理
pip freeze > requirements.txt

6.2 项目结构设计

采用分层架构,使代码结构清晰、易于扩展:

复制代码
fastapi-user-api/
├── app/
│   ├── __init__.py
│   ├── main.py                 # FastAPI 应用入口
│   ├── config.py               # 配置管理
│   ├── database.py             # 数据库连接
│   ├── models.py               # SQLAlchemy 模型
│   ├── schemas.py              # Pydantic 模式定义
│   ├── dependencies.py         # 依赖注入
│   ├── routers/
│   │   ├── __init__.py
│   │   └── users.py            # 用户路由
│   └── utils/
│       ├── __init__.py
│       └── auth.py             # 认证工具函数
├── .env
└── requirements.txt

6.3 配置管理

app/config.py

python 复制代码
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    app_name: str = "FastAPI User API"
    debug: bool = True
    database_url: str = "sqlite+aiosqlite:///./app.db"  # SQLite 开发用
    # 使用 PostgreSQL 时: database_url: str = "postgresql+asyncpg://user:pass@localhost/db"
    jwt_secret_key: str = "your-secret-key-change-in-production"
    jwt_algorithm: str = "HS256"
    access_token_expire_minutes: int = 30
    
    class Config:
        env_file = ".env"

settings = Settings()

6.4 数据库连接与模型

app/database.py

python 复制代码
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
from sqlalchemy.orm import DeclarativeBase
from .config import settings

# 创建异步数据库引擎
engine = create_async_engine(settings.database_url, echo=True)
AsyncSessionLocal = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)

class Base(DeclarativeBase):
    pass

async def get_db():
    """依赖注入函数,用于获取数据库会话"""
    async with AsyncSessionLocal() as session:
        try:
            yield session
            await session.commit()
        except Exception:
            await session.rollback()
            raise

app/models.py

python 复制代码
from sqlalchemy import Column, Integer, String, DateTime
from sqlalchemy.sql import func
from .database import Base

class User(Base):
    __tablename__ = 'users'
    
    id = Column(Integer, primary_key=True, index=True)
    username = Column(String(80), unique=True, nullable=False, index=True)
    email = Column(String(120), unique=True, nullable=False, index=True)
    password_hash = Column(String(256), nullable=False)
    created_at = Column(DateTime(timezone=True), server_default=func.now())
    updated_at = Column(DateTime(timezone=True), onupdate=func.now())

6.5 Pydantic 模式定义

app/schemas.py

python 复制代码
from pydantic import BaseModel, EmailStr, Field
from datetime import datetime
from typing import Optional

# 请求模式
class UserRegister(BaseModel):
    username: str = Field(..., min_length=3, max_length=50, description="用户名,3-50个字符")
    email: EmailStr = Field(..., description="有效的电子邮件地址")
    password: str = Field(..., min_length=6, description="密码,至少6位")

class UserLogin(BaseModel):
    username: str
    password: str

class UserUpdate(BaseModel):
    username: Optional[str] = Field(None, min_length=3, max_length=50)
    email: Optional[EmailStr] = None
    password: Optional[str] = Field(None, min_length=6)

# 响应模式
class UserResponse(BaseModel):
    id: int
    username: str
    email: str
    created_at: datetime
    
    class Config:
        from_attributes = True  # 支持从 SQLAlchemy 模型转换

class TokenResponse(BaseModel):
    access_token: str
    token_type: str = "bearer"

class UserWithTokenResponse(UserResponse):
    access_token: str

class MessageResponse(BaseModel):
    message: str

6.6 认证工具

app/utils/auth.py

python 复制代码
from passlib.context import CryptContext
from jose import jwt
from datetime import datetime, timedelta
from ..config import settings

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

def verify_password(plain_password: str, hashed_password: str) -> bool:
    return pwd_context.verify(plain_password, hashed_password)

def get_password_hash(password: str) -> str:
    return pwd_context.hash(password)

def create_access_token(data: dict):
    to_encode = data.copy()
    expire = datetime.utcnow() + timedelta(minutes=settings.access_token_expire_minutes)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, settings.jwt_secret_key, algorithm=settings.jwt_algorithm)
    return encoded_jwt

def decode_access_token(token: str):
    try:
        payload = jwt.decode(token, settings.jwt_secret_key, algorithms=[settings.jwt_algorithm])
        return payload
    except jwt.JWTError:
        return None

6.7 依赖注入与认证依赖

app/dependencies.py

python 复制代码
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from .database import get_db
from .models import User
from .utils.auth import decode_access_token

security = HTTPBearer()

async def get_current_user(
    credentials: HTTPAuthorizationCredentials = Depends(security),
    db: AsyncSession = Depends(get_db)
) -> User:
    token = credentials.credentials
    payload = decode_access_token(token)
    
    if payload is None:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid authentication credentials",
            headers={"WWW-Authenticate": "Bearer"},
        )
    
    user_id = payload.get("sub")
    if user_id is None:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid token payload"
        )
    
    result = await db.execute(select(User).where(User.id == int(user_id)))
    user = result.scalar_one_or_none()
    
    if user is None:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="User not found"
        )
    
    return user

6.8 用户路由实现

app/routers/users.py

python 复制代码
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, delete
from ..database import get_db
from ..models import User
from ..schemas import UserRegister, UserLogin, UserUpdate, UserResponse, TokenResponse, UserWithTokenResponse, MessageResponse
from ..utils.auth import get_password_hash, verify_password, create_access_token
from ..dependencies import get_current_user

router = APIRouter(prefix="/api/users", tags=["users"])

@router.post("/register", response_model=UserWithTokenResponse, status_code=status.HTTP_201_CREATED)
async def register(user_data: UserRegister, db: AsyncSession = Depends(get_db)):
    """用户注册 - 自动验证所有输入"""
    # 检查用户名是否存在
    result = await db.execute(select(User).where(User.username == user_data.username))
    if result.scalar_one_or_none():
        raise HTTPException(status_code=409, detail="Username already exists")
    
    # 检查邮箱是否存在
    result = await db.execute(select(User).where(User.email == user_data.email))
    if result.scalar_one_or_none():
        raise HTTPException(status_code=409, detail="Email already exists")
    
    # 创建新用户
    user = User(
        username=user_data.username,
        email=user_data.email,
        password_hash=get_password_hash(user_data.password)
    )
    
    db.add(user)
    await db.commit()
    await db.refresh(user)
    
    # 生成访问令牌
    access_token = create_access_token(data={"sub": str(user.id)})
    
    return UserWithTokenResponse(
        id=user.id,
        username=user.username,
        email=user.email,
        created_at=user.created_at,
        access_token=access_token
    )

@router.post("/login", response_model=UserWithTokenResponse)
async def login(login_data: UserLogin, db: AsyncSession = Depends(get_db)):
    """用户登录"""
    result = await db.execute(select(User).where(User.username == login_data.username))
    user = result.scalar_one_or_none()
    
    if not user or not verify_password(login_data.password, user.password_hash):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid username or password"
        )
    
    access_token = create_access_token(data={"sub": str(user.id)})
    
    return UserWithTokenResponse(
        id=user.id,
        username=user.username,
        email=user.email,
        created_at=user.created_at,
        access_token=access_token
    )

@router.get("/me", response_model=UserResponse)
async def get_current_user_info(current_user: User = Depends(get_current_user)):
    """获取当前登录用户信息(需要认证)"""
    return UserResponse(
        id=current_user.id,
        username=current_user.username,
        email=current_user.email,
        created_at=current_user.created_at
    )

@router.put("/me", response_model=UserResponse)
async def update_user(
    update_data: UserUpdate,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """更新当前用户信息(需要认证)"""
    if update_data.username is not None:
        # 检查新用户名是否已被占用
        result = await db.execute(select(User).where(
            User.username == update_data.username,
            User.id != current_user.id
        ))
        if result.scalar_one_or_none():
            raise HTTPException(status_code=409, detail="Username already taken")
        current_user.username = update_data.username
    
    if update_data.email is not None:
        result = await db.execute(select(User).where(
            User.email == update_data.email,
            User.id != current_user.id
        ))
        if result.scalar_one_or_none():
            raise HTTPException(status_code=409, detail="Email already taken")
        current_user.email = update_data.email
    
    if update_data.password is not None:
        current_user.password_hash = get_password_hash(update_data.password)
    
    await db.commit()
    await db.refresh(current_user)
    
    return UserResponse(
        id=current_user.id,
        username=current_user.username,
        email=current_user.email,
        created_at=current_user.created_at
    )

@router.delete("/me", response_model=MessageResponse)
async def delete_user(
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """删除当前用户(需要认证)"""
    await db.execute(delete(User).where(User.id == current_user.id))
    await db.commit()
    
    return MessageResponse(message="User deleted successfully")

6.9 应用入口

app/main.py

python 复制代码
from fastapi import FastAPI
from contextlib import asynccontextmanager
from .config import settings
from .database import engine, Base
from .routers import users

@asynccontextmanager
async def lifespan(app: FastAPI):
    # 启动时:创建数据库表
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)
    yield
    # 关闭时:清理资源
    await engine.dispose()

app = FastAPI(
    title=settings.app_name,
    description="完整的用户管理 REST API 示例 - 展示 FastAPI 的核心特性",
    version="1.0.0",
    docs_url="/docs",
    redoc_url="/redoc",
    lifespan=lifespan
)

# 注册路由
app.include_router(users.router)

@app.get("/")
async def root():
    return {"message": "Welcome to FastAPI User API", "docs_url": "/docs"}

@app.get("/health")
async def health_check():
    return {"status": "ok"}

6.10 运行与测试

bash 复制代码
# 启动 FastAPI 应用
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000

启动后即可访问交互式 API 文档:

FastAPI 无需任何额外配置就会自动生成这两个文档界面,这是它与 Flask 最显著的体验差异之一。开发者编写完代码后,文档即自动生成,API 的使用者可以直接通过 Swagger UI 交互式地测试所有端点。

6.11 API 测试示例

在 Swagger UI 中完成完整的用户流:

  1. POST /api/users/register → 提交用户名、邮箱、密码进行注册
  2. POST /api/users/login → 使用用户名和密码登录,获取 access_token
  3. 点击 Swagger UI 右上角的 "Authorize" 按钮 → 输入 Bearer <access_token>
  4. GET /api/users/me → 获取当前用户信息
  5. PUT /api/users/me → 更新用户信息
  6. DELETE /api/users/me → 删除用户

所有请求的验证都是自动完成的。例如,在注册时如果邮箱格式不正确,FastAPI 会自动返回带有详细字段信息的 422 响应;如果密码长度不足 6 位,同样会得到明确的错误反馈。

七、总结与选型建议

7.1 综合对比

评估维度 Flask FastAPI
适用场景 小型 Web 应用、原型开发、CMS、传统网站 高性能 API、微服务、实时应用、AI 服务
学习成本 低------代码直观,几乎无需额外概念 中------需理解 async/await、类型提示和 Pydantic
开发效率 中------手动集成组件需要多一些代码 极高------自动文档、自动验证大幅减少样板代码
生产就绪 成熟------经过十余年大规模验证 快速增长中------已被多家大型企业采用
社区生态 庞大------拥有数千个成熟扩展 活跃------新扩展增长迅速

7.2 选型决策树

推荐 Flask 的场景:

  • 团队有深厚的 Flask 经验和现有的 Flask 代码库
  • 项目主要做传统 Web 应用而非 API,需要 Jinja2 模板
  • 对性能要求不高,API 调用量较少
  • 偏好轻量灵活的架构,喜欢自己选择组件
  • 项目较小,追求最低的学习曲线

推荐 FastAPI 的场景:

  • 构建纯 API 服务或微服务架构
  • 预计有高并发请求或 I/O 密集型任务
  • 团队重视生产力,希望减少重复代码
  • 需要为前端或第三方提供清晰、自动生成的 API 文档
  • 涉及异步处理(如 WebSocket、实时通信)
  • 追求现代化开发体验和最佳性能

Flask 和 FastAPI 并非对立关系。在 Python Web 开发中,两者各有擅长的领域,了解各自的优劣并根据项目需求做出选择,才是高效开发的关键。无论最终选择哪一个框架,本文提供的手把手教程都已覆盖了核心概念和开发实践,可以作为快速上手的参考起始点。

相关推荐
测试员周周1 小时前
【Appium 系列】第04节-Page Object 模式 — BasePage 基类设计
开发语言·数据库·人工智能·python·语言模型·appium·web app
无限中终1 小时前
如何抓取某音视频的互动数据
爬虫·python
坐吃山猪1 小时前
Python34_装饰器知识
开发语言·python·ubuntu
ZHW_AI课题组1 小时前
调用华为智能云API实现手写图片识别
图像处理·python·机器学习·华为·分类
weixin_660096781 小时前
【无标题】
python·debugpy
小草cys1 小时前
Anaconda 的虚拟环境(envs)从默认的 C 盘迁移到其他磁盘
开发语言·python·anaconda
keineahnung23451 小时前
PyTorch SymNode 的 _is_contiguous 從何而來?──sizes_strides_impl 實作詳解
人工智能·pytorch·python·深度学习
七牛云行业应用1 小时前
MCP 服务器本地部署实战【2026】:Python/Node.js 搭建 + Claude/Cursor/TRAE
服务器·python·node.js
Web极客码1 小时前
Python Deque:构建实时滑动窗口与高性能缓存的“隐藏高手”
java·python·缓存