一、引言
在 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 文档:
- Swagger UI:http://localhost:8000/docs ------ 可直接在浏览器中测试所有 API 端点
- ReDoc:http://localhost:8000/redoc ------ 整洁详细的 API 文档页面
FastAPI 无需任何额外配置就会自动生成这两个文档界面,这是它与 Flask 最显著的体验差异之一。开发者编写完代码后,文档即自动生成,API 的使用者可以直接通过 Swagger UI 交互式地测试所有端点。
6.11 API 测试示例
在 Swagger UI 中完成完整的用户流:
- POST /api/users/register → 提交用户名、邮箱、密码进行注册
- POST /api/users/login → 使用用户名和密码登录,获取 access_token
- 点击 Swagger UI 右上角的 "Authorize" 按钮 → 输入
Bearer <access_token> - GET /api/users/me → 获取当前用户信息
- PUT /api/users/me → 更新用户信息
- 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 开发中,两者各有擅长的领域,了解各自的优劣并根据项目需求做出选择,才是高效开发的关键。无论最终选择哪一个框架,本文提供的手把手教程都已覆盖了核心概念和开发实践,可以作为快速上手的参考起始点。