摘要钩子:还在为API接口性能发愁吗?想体验Python异步编程的极致性能吗?本文带你从零开始掌握FastAPI,这个2025年Python后端开发的主流选择!通过手把手实战,你将学会用不到100行代码构建高性能RESTful API,体验比Flask快5倍的性能提升,轻松应对高并发场景!
开篇:为什么2025年的后端开发者都在学FastAPI?
最近和几位做后端的朋友聊天,发现一个有趣的现象:以前大家见面都问"你还在用Flask吗?",现在变成了"你FastAPI学到哪了?"。这不是偶然——根据2025年的开发者调查报告,FastAPI已经超越Flask,成为Python Web框架的新宠。
但很多刚接触FastAPI的朋友都会遇到这样的困惑:
- 异步编程听起来很高级,但async/await到底怎么用?
- Pydantic模型和普通Python类有什么区别?
- 为什么FastAPI的文档能自动生成,怎么定制?
- 从Flask/Django转过来,项目结构该怎么组织?
- 生产环境部署有哪些坑要提前避开?
如果你也有这些疑问,那么恭喜你找到了正确的学习路径。本文将用最接地气的方式,带你从零开始掌握FastAPI的核心技能,让你不仅能写出高性能API,还能理解背后的设计哲学。
先来看一个真实的性能对比:某电商平台商品查询接口,日均调用量1000万次。使用Flask同步实现,平均响应时间120ms,服务器需要8台;迁移到FastAPI异步实现后,平均响应时间降低到25ms,服务器减少到2台。这就是异步编程的魅力!
第一部分:环境搭建与第一个异步API
1.1 选择最稳的环境配置方案
2025年的Python开发环境已经有了很大变化,这里推荐三种方案,你可以根据自己的情况选择:
方案一:现代极速方案(推荐给追求效率的开发者)
# 安装uv,比pip快10-100倍的包管理工具
curl -LsSf https://astral.sh/uv/install.sh | sh
# 创建虚拟环境
uv venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# 安装FastAPI全家桶
uv pip install fastapi[standard] uvicorn[standard]
方案二:经典稳定方案(适合企业级项目)
# 使用Python内置venv
python -m venv fastapi_env
source fastapi_env/bin/activate # Windows: fastapi_env\Scripts\activate
# 升级pip并安装
pip install --upgrade pip
pip install fastapi==0.127.0 uvicorn[standard]
方案三:容器化方案(适合微服务和云原生)
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
安装完成后,用这个命令验证:
bash
python -c "import fastapi; print(f'FastAPI版本: {fastapi.__version__}')"
1.2 10行代码:你的第一个异步API
别被"异步"两个字吓到,其实FastAPI的入门非常简单。创建一个main.py文件:
# main.py
from fastapi import FastAPI
# 创建FastAPI应用实例
app = FastAPI(
title="我的第一个FastAPI应用",
description="体验异步编程的魅力",
version="1.0.0"
)
@app.get("/")
async def read_root():
"""首页接口"""
return {"message": "欢迎来到FastAPI世界!"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str = None):
"""带路径参数和查询参数的接口"""
return {"item_id": item_id, "query": q}
启动服务:
uvicorn main:app --reload --port 8000
现在打开浏览器访问:
- API文档:
http://localhost:8000/docs(Swagger UI) - 接口测试:
http://localhost:8000/items/42?q=test
看到自动生成的交互式文档了吗?这就是FastAPI的第一个魔法:自动文档生成。你写的每个函数、每个参数、每个返回类型,FastAPI都自动帮你生成了可交互的API文档。
1.3 理解async/await:异步编程的核心
很多朋友对异步编程感到困惑,其实用一个生活例子就能讲明白:
同步 vs 异步的日常比喻:
- 同步:就像在餐厅点餐,你必须等厨师做好一道菜,才能点下一道
- 异步:就像在快餐店点餐,你点完餐拿到号码,可以去干别的事,餐做好了服务员会叫号
在代码中:
# 同步版本(Flask风格)
def get_user_sync(user_id):
# 模拟耗时的数据库查询
time.sleep(1) # 阻塞1秒
return {"id": user_id, "name": "张三"}
# 异步版本(FastAPI风格)
async def get_user_async(user_id):
# 异步数据库查询,不阻塞其他请求
await asyncio.sleep(1) # 非阻塞等待1秒
return {"id": user_id, "name": "张三"}
关键区别:
- 同步函数:使用
def定义,遇到IO操作(数据库、网络请求)时会阻塞整个线程 - 异步函数:使用
async def定义,遇到IO操作时使用await挂起,让CPU去处理其他请求
什么时候用async/await?
- ✅ 数据库查询、API调用、文件读写等IO密集型操作
- ✅ 需要并发处理大量请求的场景
- ❌ CPU密集型计算(如图像处理、复杂算法)
第二部分:数据验证与类型安全
2.1 Pydantic模型:告别手写验证代码
传统Web开发中最头疼的就是数据验证。回想一下,你在Flask或Django中写过多少这样的代码:
# Flask风格的手动验证
def create_user():
data = request.get_json()
# 手动验证每个字段
if not data.get('username'):
return {"error": "用户名不能为空"}, 400
if len(data.get('username', '')) < 3:
return {"error": "用户名至少3个字符"}, 400
if not re.match(r'^[a-zA-Z0-9_]+$', data.get('username', '')):
return {"error": "用户名只能包含字母数字和下划线"}, 400
# ... 还有邮箱、密码等一堆验证
在FastAPI中,这一切都可以用Pydantic模型优雅解决:
# models.py
from pydantic import BaseModel, EmailStr, Field, validator
from typing import Optional
from datetime import datetime
class UserBase(BaseModel):
username: str = Field(..., min_length=3, max_length=50,
pattern=r'^[a-zA-Z0-9_]+$',
example="john_doe")
email: EmailStr
class UserCreate(UserBase):
password: str = Field(..., min_length=8,
description="密码必须包含字母和数字",
regex=r'^(?=.*[A-Za-z])(?=.*\d).{8,}$')
@validator('username')
def username_not_admin(cls, v):
if v.lower() == 'admin':
raise ValueError('用户名不能是admin')
return v
class UserResponse(UserBase):
id: int
created_at: datetime
is_active: bool = True
class Config:
from_attributes = True # 替代原来的orm_mode
然后在路由中使用:
# main.py
from fastapi import FastAPI, HTTPException, status
from models import UserCreate, UserResponse
app = FastAPI()
users_db = []
@app.post("/users/", response_model=UserResponse,
status_code=status.HTTP_201_CREATED)
async def create_user(user: UserCreate):
"""创建用户接口"""
# FastAPI已经自动验证了user参数
# 如果数据不符合模型要求,会返回422错误
# 模拟保存到数据库
db_user = {
"id": len(users_db) + 1,
"username": user.username,
"email": user.email,
"password_hash": f"hashed_{user.password}",
"created_at": datetime.now(),
"is_active": True
}
users_db.append(db_user)
return db_user
Pydantic的优势:
- 自动验证:类型、长度、正则、自定义验证器
- 自动文档:字段描述、示例值自动显示在Swagger中
- 自动转换:JSON ↔ Python对象双向转换
- IDE友好:完整的类型提示,智能补全
2.2 请求参数全解析:路径、查询、请求体
FastAPI支持多种参数传递方式,理解它们的区别很重要:
from fastapi import FastAPI, Query, Path, Body
from typing import Optional, List
from models import Item
app = FastAPI()
# 1. 路径参数
@app.get("/items/{item_id}")
async def read_item(
item_id: int = Path(..., title="商品ID", ge=1, le=1000),
# ge=大于等于, le=小于等于, 自动验证
):
return {"item_id": item_id}
# 2. 查询参数
@app.get("/search/")
async def search_items(
keyword: str,
category: Optional[str] = Query(None, min_length=2),
min_price: float = Query(0, ge=0),
max_price: float = Query(10000, le=100000),
tags: List[str] = Query([], description="商品标签")
):
return {
"keyword": keyword,
"filters": {
"category": category,
"price_range": [min_price, max_price],
"tags": tags
}
}
# 3. 请求体参数
@app.post("/items/")
async def create_item(
item: Item,
# 单个请求体,FastAPI会自动从JSON解析
):
return item
# 4. 混合参数:路径 + 查询 + 多个请求体
@app.put("/items/{item_id}")
async def update_item(
item_id: int,
item: Item,
importance: int = Body(..., ge=1, le=10),
# Body用于指定这是请求体字段
):
return {
"item_id": item_id,
"item": item,
"importance": importance
}
参数优先级规则:
- 路径参数:URL路径中的部分,如
/items/{item_id} - 查询参数:URL中
?后面的部分,如?category=electronics - 请求体:POST/PUT请求中的JSON数据
- 表单数据:
application/x-www-form-urlencoded格式
2.3 响应模型与状态码控制
好的API设计不仅要处理输入,还要规范输出:
from fastapi import FastAPI, status, HTTPException
from models import UserResponse, UserCreate
from typing import List
app = FastAPI()
# 指定响应模型
@app.post("/users/", response_model=UserResponse,
status_code=status.HTTP_201_CREATED,
summary="创建用户",
description="创建一个新用户账户",
response_description="返回创建的用户信息")
async def create_user(user: UserCreate):
# 业务逻辑...
return db_user
# 错误响应
@app.get("/users/{user_id}", response_model=UserResponse)
async def get_user(user_id: int):
user = find_user_by_id(user_id)
if not user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="用户不存在",
headers={"X-Error": "用户ID无效"}
)
return user
# 自定义响应
from fastapi.responses import JSONResponse, HTMLResponse
@app.get("/custom", response_class=JSONResponse)
async def custom_response():
return JSONResponse(
content={"message": "自定义响应"},
status_code=202,
headers={"X-Custom": "Header"}
)
@app.get("/html", response_class=HTMLResponse)
async def html_response():
return """
<html>
<head><title>HTML响应</title></head>
<body><h1>Hello FastAPI!</h1></body>
</html>
"""
状态码使用指南:
200 OK:获取资源成功201 Created:创建资源成功204 No Content:删除成功,无返回内容400 Bad Request:客户端请求错误401 Unauthorized:需要认证403 Forbidden:无权限404 Not Found:资源不存在422 Unprocessable Entity:数据验证失败500 Internal Server Error:服务器内部错误
第三部分:依赖注入与项目架构
3.1 依赖注入:解耦业务逻辑的利器
依赖注入是FastAPI最强大的特性之一。想象一下,你的每个API接口都需要数据库连接、用户认证、权限检查,如果每个函数都自己处理,代码会变得臃肿不堪。
传统写法(紧耦合) :
def get_user(user_id):
# 每个函数都自己创建数据库连接
db = get_db_connection()
user = db.query(User).filter(User.id == user_id).first()
db.close()
return user
def create_order(order_data):
# 重复的数据库连接代码
db = get_db_connection()
order = Order(**order_data)
db.add(order)
db.commit()
db.close()
return order
**FastAPI依赖注入写法(松耦合) **:
# dependencies.py
from fastapi import Depends, HTTPException, status
from typing import Annotated
# 1. 数据库依赖
async def get_db():
"""数据库会话依赖"""
db = SessionLocal()
try:
yield db
finally:
db.close()
# 2. 认证依赖
async def get_current_user(
token: str = Depends(oauth2_scheme),
db: Session = Depends(get_db)
):
"""获取当前登录用户"""
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="无效的认证凭证",
headers={"WWW-Authenticate": "Bearer"},
)
# 验证token
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
user = db.query(User).filter(User.username == username).first()
if user is None:
raise credentials_exception
return user
# 3. 权限依赖
async def require_admin(
current_user: User = Depends(get_current_user)
):
"""要求管理员权限"""
if not current_user.is_admin:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="需要管理员权限"
)
return current_user
# 类型别名,方便使用
DatabaseSession = Annotated[Session, Depends(get_db)]
CurrentUser = Annotated[User, Depends(get_current_user)]
AdminUser = Annotated[User, Depends(require_admin)]
在路由中使用:
# routers/users.py
from fastapi import APIRouter, Depends, status
from dependencies import DatabaseSession, CurrentUser, AdminUser
from models import UserResponse
router = APIRouter(prefix="/users", tags=["用户管理"])
@router.get("/me", response_model=UserResponse)
async def read_users_me(current_user: CurrentUser):
"""获取当前用户信息"""
return current_user
@router.get("/", response_model=list[UserResponse])
async def list_users(
db: DatabaseSession,
admin: AdminUser # 只有管理员能调用
):
"""列出所有用户(需要管理员权限)"""
return db.query(User).all()
@router.post("/", response_model=UserResponse,
status_code=status.HTTP_201_CREATED)
async def create_user(
user_data: UserCreate,
db: DatabaseSession,
admin: AdminUser
):
"""创建用户(需要管理员权限)"""
# 检查用户名是否已存在
existing = db.query(User).filter(User.username == user_data.username).first()
if existing:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="用户名已存在"
)
# 创建用户
db_user = User(** user_data.dict())
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
依赖注入的优势:
- 代码复用:一次定义,多处使用
- 易于测试:可以轻松模拟依赖
- 关注点分离:业务逻辑更清晰
- 类型安全:完整的类型提示
3.2 企业级项目结构设计
当项目规模变大时,良好的目录结构至关重要。这是2025年推荐的企业级FastAPI项目结构:
fastapi_project/
├── app/
│ ├── __init__.py
│ ├── main.py # 应用入口
│ ├── config.py # 配置文件
│ ├── database.py # 数据库配置
│ ├── models.py # Pydantic模型
│ ├── schemas.py # 数据库模型
│ ├── dependencies.py # 依赖注入
│ ├── middleware.py # 中间件
│ ├── routers/ # 路由模块
│ │ ├── __init__.py
│ │ ├── auth.py # 认证路由
│ │ ├── users.py # 用户路由
│ │ ├── items.py # 商品路由
│ │ └── orders.py # 订单路由
│ ├── services/ # 业务逻辑层
│ │ ├── __init__.py
│ │ ├── auth_service.py
│ │ ├── user_service.py
│ │ └── item_service.py
│ └── utils/ # 工具函数
│ ├── __init__.py
│ ├── security.py # 安全相关
│ └── validators.py # 验证器
├── tests/ # 测试目录
│ ├── __init__.py
│ ├── conftest.py
│ ├── test_auth.py
│ └── test_users.py
├── alembic/ # 数据库迁移
│ ├── versions/
│ └── alembic.ini
├── requirements/
│ ├── base.txt # 基础依赖
│ ├── dev.txt # 开发依赖
│ └── prod.txt # 生产依赖
├── .env.example # 环境变量示例
├── docker-compose.yml # Docker编排
├── Dockerfile # Docker配置
├── pyproject.toml # 项目配置
└── README.md # 项目说明
这种模块化的项目结构让代码更易维护和扩展。下面是实际的项目架构示意图:
核心文件示例:
-
app/main.py - 应用入口:
from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from app.routers import auth, users, items, orders from app.middleware import audit_middleware from app.config import settings
app = FastAPI( title=settings.APP_NAME, version=settings.APP_VERSION, openapi_url=f"{settings.API_PREFIX}/openapi.json" )
中间件
app.add_middleware(CORSMiddleware, **settings.CORS_CONFIG) app.middleware("http")(audit_middleware)
路由注册
app.include_router(auth.router, prefix=settings.API_PREFIX) app.include_router(users.router, prefix=settings.API_PREFIX) app.include_router(items.router, prefix=settings.API_PREFIX) app.include_router(orders.router, prefix=settings.API_PREFIX)
@app.get("/health") async def health_check(): return {"status": "healthy"}
-
app/database.py - 数据库配置:
from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from app.config import settings
异步数据库引擎(SQLAlchemy 2.0)
engine = create_engine( settings.DATABASE_URL, pool_pre_ping=True, pool_size=20, max_overflow=30, echo=settings.DEBUG )
SessionLocal = sessionmaker( autocommit=False, autoflush=False, bind=engine )
Base = declarative_base()
依赖函数
def get_db(): db = SessionLocal() try: yield db finally: db.close()
-
app/config.py - 配置文件:
from pydantic_settings import BaseSettings
class Settings(BaseSettings): APP_NAME: str = "FastAPI企业应用" APP_VERSION: str = "1.0.0" DEBUG: bool = False
# 数据库 DATABASE_URL: str = "postgresql://user:pass@localhost/dbname" # 安全 SECRET_KEY: str = "your-secret-key-change-in-production" ALGORITHM: str = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES: int = 30 # API API_PREFIX: str = "/api/v1" # CORS CORS_ORIGINS: list = ["http://localhost:3000"] class Config: env_file = ".env"settings = Settings()
3.3 中间件与异常处理
中间件是处理请求和响应的强大工具:
# app/middleware.py
import time
import logging
from fastapi import Request, Response
from fastapi.responses import JSONResponse
logger = logging.getLogger("fastapi")
async def audit_middleware(request: Request, call_next):
"""审计中间件:记录请求日志"""
start_time = time.time()
try:
response = await call_next(request)
# 计算处理时间
process_time = time.time() - start_time
# 记录日志
log_data = {
"method": request.method,
"path": request.url.path,
"status": response.status_code,
"duration": round(process_time, 3),
"client": request.client.host if request.client else "unknown",
"user_agent": request.headers.get("user-agent", "")
}
logger.info(f"请求处理完成: {log_data}")
# 添加自定义响应头
response.headers["X-Process-Time"] = str(process_time)
return response
except Exception as e:
# 异常处理
logger.error(f"请求处理异常: {str(e)}", exc_info=True)
return JSONResponse(
status_code=500,
content={"error": "内部服务器错误"}
)
# 自定义异常处理器
from fastapi.exceptions import RequestValidationError
from starlette.exceptions import HTTPException as StarletteHTTPException
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
"""处理数据验证异常"""
errors = []
for error in exc.errors():
errors.append({
"field": ".".join(str(loc) for loc in error["loc"]),
"message": error["msg"],
"type": error["type"]
})
return JSONResponse(
status_code=422,
content={
"error": "数据验证失败",
"details": errors
}
)
@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request, exc):
"""处理HTTP异常"""
return JSONResponse(
status_code=exc.status_code,
content={
"error": exc.detail
}
)
第四部分:实战项目:待办事项API系统
现在让我们把前面学到的知识综合起来,构建一个完整的待办事项API系统。
4.1 项目需求分析
我们要开发一个简单的待办事项管理系统,包含以下功能:
- 用户注册、登录、JWT认证
- 待办事项的增删改查
- 待办事项的状态管理(待完成/已完成)
- 数据验证和权限控制
- 自动API文档
4.2 数据库模型设计
# app/schemas.py
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey
from sqlalchemy.orm import relationship
from app.database import Base
import datetime
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String(50), unique=True, index=True, nullable=False)
email = Column(String(100), unique=True, index=True, nullable=False)
password_hash = Column(String(255), nullable=False)
is_active = Column(Boolean, default=True)
is_admin = Column(Boolean, default=False)
created_at = Column(DateTime, default=datetime.datetime.utcnow)
# 关系
todos = relationship("Todo", back_populates="owner")
class Todo(Base):
__tablename__ = "todos"
id = Column(Integer, primary_key=True, index=True)
title = Column(String(200), nullable=False)
description = Column(String(500))
completed = Column(Boolean, default=False)
created_at = Column(DateTime, default=datetime.datetime.utcnow)
updated_at = Column(DateTime, default=datetime.datetime.utcnow,
onupdate=datetime.datetime.utcnow)
# 外键
owner_id = Column(Integer, ForeignKey("users.id"))
# 关系
owner = relationship("User", back_populates="todos")
4.3 Pydantic模型定义
# app/models.py
from pydantic import BaseModel, EmailStr, Field, validator
from typing import Optional
from datetime import datetime
# 用户相关模型
class UserBase(BaseModel):
username: str = Field(..., min_length=3, max_length=50)
email: EmailStr
class UserCreate(UserBase):
password: str = Field(..., min_length=8)
class UserLogin(BaseModel):
username: str
password: str
class UserResponse(UserBase):
id: int
is_active: bool
created_at: datetime
class Config:
from_attributes = True
class Token(BaseModel):
access_token: str
token_type: str = "bearer"
# 待办事项模型
class TodoBase(BaseModel):
title: str = Field(..., min_length=1, max_length=200)
description: Optional[str] = Field(None, max_length=500)
class TodoCreate(TodoBase):
pass
class TodoUpdate(BaseModel):
title: Optional[str] = Field(None, min_length=1, max_length=200)
description: Optional[str] = Field(None, max_length=500)
completed: Optional[bool] = None
class TodoResponse(TodoBase):
id: int
completed: bool
created_at: datetime
updated_at: datetime
owner_id: int
class Config:
from_attributes = True
4.4 路由实现
4.4.1 认证路由
# app/routers/auth.py
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from datetime import datetime, timedelta
import jwt
from passlib.context import CryptContext
from app.database import get_db
from app.models import UserCreate, UserLogin, Token, UserResponse
from app.schemas import User
from sqlalchemy.orm import Session
from typing import Annotated
router = APIRouter(tags=["认证"])
# 安全配置
SECRET_KEY = "your-secret-key-change-in-production"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="auth/token")
# 工具函数
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password):
return pwd_context.hash(password)
def create_access_token(data: dict, expires_delta: timedelta = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
# 依赖函数
async def get_current_user(
token: Annotated[str, Depends(oauth2_scheme)],
db: Annotated[Session, Depends(get_db)]
):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="无效的认证凭证",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except jwt.PyJWTError:
raise credentials_exception
user = db.query(User).filter(User.username == username).first()
if user is None:
raise credentials_exception
return user
# 路由
@router.post("/register", response_model=UserResponse,
status_code=status.HTTP_201_CREATED)
async def register(
user_data: UserCreate,
db: Annotated[Session, Depends(get_db)]
):
"""用户注册"""
# 检查用户名是否已存在
existing_user = db.query(User).filter(
(User.username == user_data.username) |
(User.email == user_data.email)
).first()
if existing_user:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="用户名或邮箱已存在"
)
# 创建用户
db_user = User(
username=user_data.username,
email=user_data.email,
password_hash=get_password_hash(user_data.password)
)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
@router.post("/token", response_model=Token)
async def login_for_access_token(
form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
db: Annotated[Session, Depends(get_db)]
):
"""获取访问令牌"""
# 查找用户
user = db.query(User).filter(User.username == form_data.username).first()
if not user or not verify_password(form_data.password, user.password_hash):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="用户名或密码错误",
headers={"WWW-Authenticate": "Bearer"},
)
# 创建token
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username},
expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}
@router.get("/me", response_model=UserResponse)
async def read_users_me(
current_user: Annotated[User, Depends(get_current_user)]
):
"""获取当前用户信息"""
return current_user
4.4.2 待办事项路由
# app/routers/todos.py
from fastapi import APIRouter, Depends, HTTPException, status
from typing import Annotated, List
from sqlalchemy.orm import Session
from app.database import get_db
from app.models import TodoCreate, TodoUpdate, TodoResponse
from app.schemas import Todo, User
from app.routers.auth import get_current_user
router = APIRouter(prefix="/todos", tags=["待办事项"])
# 依赖:确保用户只能操作自己的待办事项
async def get_todo_or_404(
todo_id: int,
db: Annotated[Session, Depends(get_db)],
current_user: Annotated[User, Depends(get_current_user)]
) -> Todo:
todo = db.query(Todo).filter(
Todo.id == todo_id,
Todo.owner_id == current_user.id
).first()
if not todo:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="待办事项不存在或无权限访问"
)
return todo
@router.get("/", response_model=List[TodoResponse])
async def list_todos(
db: Annotated[Session, Depends(get_db)],
current_user: Annotated[User, Depends(get_current_user)],
skip: int = 0,
limit: int = 100,
completed: bool = None
):
"""获取待办事项列表"""
query = db.query(Todo).filter(Todo.owner_id == current_user.id)
if completed is not None:
query = query.filter(Todo.completed == completed)
todos = query.offset(skip).limit(limit).all()
return todos
@router.get("/{todo_id}", response_model=TodoResponse)
async def get_todo(
todo: Annotated[Todo, Depends(get_todo_or_404)]
):
"""获取单个待办事项"""
return todo
@router.post("/", response_model=TodoResponse,
status_code=status.HTTP_201_CREATED)
async def create_todo(
todo_data: TodoCreate,
db: Annotated[Session, Depends(get_db)],
current_user: Annotated[User, Depends(get_current_user)]
):
"""创建待办事项"""
db_todo = Todo(
**todo_data.dict(),
owner_id=current_user.id
)
db.add(db_todo)
db.commit()
db.refresh(db_todo)
return db_todo
@router.put("/{todo_id}", response_model=TodoResponse)
async def update_todo(
todo_update: TodoUpdate,
todo: Annotated[Todo, Depends(get_todo_or_404)],
db: Annotated[Session, Depends(get_db)]
):
"""更新待办事项"""
update_data = todo_update.dict(exclude_unset=True)
for field, value in update_data.items():
setattr(todo, field, value)
db.add(todo)
db.commit()
db.refresh(todo)
return todo
@router.delete("/{todo_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_todo(
todo: Annotated[Todo, Depends(get_todo_or_404)],
db: Annotated[Session, Depends(get_db)]
):
"""删除待办事项"""
db.delete(todo)
db.commit()
4.5 完整应用入口
# app/main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.routers import auth, todos
from app.middleware import audit_middleware
from app.config import settings
import logging
# 配置日志
logging.basicConfig(
level=logging.INFO if not settings.DEBUG else logging.DEBUG,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
app = FastAPI(
title=settings.APP_NAME,
description="待办事项API系统",
version=settings.APP_VERSION,
docs_url="/docs",
redoc_url="/redoc"
)
# CORS配置
app.add_middleware(
CORSMiddleware,
allow_origins=settings.CORS_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 自定义中间件
app.middleware("http")(audit_middleware)
# 注册路由
app.include_router(auth.router, prefix="/auth")
app.include_router(todos.router, prefix="/api")
@app.get("/")
async def root():
"""根路径"""
return {
"message": "欢迎使用待办事项API系统",
"docs": "/docs",
"health": "/health"
}
@app.get("/health")
async def health_check():
"""健康检查"""
return {"status": "healthy", "timestamp": datetime.utcnow()}
4.6 运行与测试
-
启动应用:
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
-
访问文档:
- Swagger UI:
http://localhost:8000/docs - ReDoc:
http://localhost:8000/redoc
-
测试API:
1. 注册用户
curl -X POST "http://localhost:8000/auth/register"
-H "Content-Type: application/json"
-d '{"username":"testuser","email":"test@example.com","password":"Test1234"}'2. 登录获取token
curl -X POST "http://localhost:8000/auth/token"
-H "Content-Type: application/x-www-form-urlencoded"
-d "username=testuser&password=Test1234"3. 创建待办事项(使用获取的token)
curl -X POST "http://localhost:8000/api/todos/"
-H "Content-Type: application/json"
-H "Authorization: Bearer YOUR_TOKEN_HERE"
-d '{"title":"学习FastAPI","description":"完成第一篇教程"}'4. 获取待办事项列表
curl -X GET "http://localhost:8000/api/todos/"
-H "Authorization: Bearer YOUR_TOKEN_HERE"
进阶实战:FastAPI高级特性探索
6.1 WebSocket实时通信
FastAPI对WebSocket提供了原生支持,非常适合实时应用:
# app/routers/websocket.py
from fastapi import APIRouter, WebSocket, WebSocketDisconnect
from typing import List
import json
router = APIRouter()
class ConnectionManager:
def __init__(self):
self.active_connections: List[WebSocket] = []
async def connect(self, websocket: WebSocket):
await websocket.accept()
self.active_connections.append(websocket)
def disconnect(self, websocket: WebSocket):
self.active_connections.remove(websocket)
async def send_personal_message(self, message: str, websocket: WebSocket):
await websocket.send_text(message)
async def broadcast(self, message: str):
for connection in self.active_connections:
try:
await connection.send_text(message)
except:
pass
manager = ConnectionManager()
@router.websocket("/ws/{client_id}")
async def websocket_endpoint(websocket: WebSocket, client_id: str):
await manager.connect(websocket)
try:
while True:
data = await websocket.receive_text()
message = json.dumps({
"client_id": client_id,
"message": data,
"timestamp": datetime.utcnow().isoformat()
})
await manager.broadcast(message)
except WebSocketDisconnect:
manager.disconnect(websocket)
await manager.broadcast(f"客户端 {client_id} 已断开连接")
6.2 文件上传与下载
FastAPI简化了文件处理流程:
from fastapi import UploadFile, File
from fastapi.responses import FileResponse
import shutil
import os
@app.post("/upload/")
async def upload_file(file: UploadFile = File(...)):
"""上传文件"""
# 保存文件
file_path = f"uploads/{file.filename}"
with open(file_path, "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
return {
"filename": file.filename,
"content_type": file.content_type,
"size": os.path.getsize(file_path)
}
@app.get("/download/{filename}")
async def download_file(filename: str):
"""下载文件"""
file_path = f"uploads/{filename}"
if os.path.exists(file_path):
return FileResponse(
path=file_path,
filename=filename,
media_type='application/octet-stream'
)
return {"error": "文件不存在"}
6.3 后台任务处理
对于耗时操作,使用BackgroundTasks避免阻塞:
from fastapi import BackgroundTasks
import time
def send_email_background(email: str, message: str):
"""后台发送邮件"""
time.sleep(2) # 模拟发送邮件耗时
print(f"邮件已发送至 {email}: {message}")
@app.post("/send-email/")
async def send_email(
email: str,
message: str,
background_tasks: BackgroundTasks
):
"""发送邮件接口"""
background_tasks.add_task(
send_email_background, email, message
)
return {"status": "邮件发送任务已提交"}
6.4 自定义中间件高级用法
创建更复杂的中间件处理:
from fastapi import Request
import uuid
import time
@app.middleware("http")
async def request_id_middleware(request: Request, call_next):
"""为每个请求添加唯一ID"""
request_id = str(uuid.uuid4())
request.state.request_id = request_id
# 添加请求ID到请求头
request.headers.__dict__["_list"].append(
(b"x-request-id", request_id.encode())
)
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
# 添加处理时间到响应头
response.headers["X-Process-Time"] = str(process_time)
response.headers["X-Request-ID"] = request_id
return response
@app.get("/debug/")
async def debug_request(request: Request):
"""调试接口,展示请求信息"""
return {
"request_id": request.state.request_id,
"client": request.client.host if request.client else None,
"headers": dict(request.headers)
}
第五部分:部署与性能优化
5.1 生产环境部署配置
# gunicorn_conf.py
import multiprocessing
# 服务器配置
bind = "0.0.0.0:8000"
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "uvicorn.workers.UvicornWorker"
# 性能优化
keepalive = 60
timeout = 120
graceful_timeout = 30
# 日志配置
accesslog = "-"
errorlog = "-"
loglevel = "info"
# 进程名称
proc_name = "fastapi_app"
# docker-compose.prod.yml
version: '3.8'
services:
api:
build:
context: .
dockerfile: Dockerfile.prod
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/todos
- SECRET_KEY=${SECRET_KEY}
- DEBUG=False
depends_on:
- db
restart: unless-stopped
networks:
- app-network
db:
image: postgres:15-alpine
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=todos
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- app-network
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on:
- api
networks:
- app-network
restart: unless-stopped
networks:
app-network:
driver: bridge
volumes:
postgres_data:
5.2 性能监控与优化
# app/monitoring.py
import time
from prometheus_client import Counter, Histogram, generate_latest
from fastapi import Request, Response
from fastapi.responses import PlainTextResponse
# 定义指标
REQUEST_COUNT = Counter(
'http_requests_total',
'Total HTTP requests',
['method', 'endpoint', 'status']
)
REQUEST_LATENCY = Histogram(
'http_request_duration_seconds',
'HTTP request latency',
['method', 'endpoint']
)
# 中间件
async def monitor_requests(request: Request, call_next):
"""监控请求性能"""
start_time = time.time()
try:
response = await call_next(request)
# 计算延迟
latency = time.time() - start_time
# 记录指标
REQUEST_COUNT.labels(
method=request.method,
endpoint=request.url.path,
status=response.status_code
).inc()
REQUEST_LATENCY.labels(
method=request.method,
endpoint=request.url.path
).observe(latency)
return response
except Exception as e:
# 记录异常
REQUEST_COUNT.labels(
method=request.method,
endpoint=request.url.path,
status=500
).inc()
raise e
# Prometheus指标端点
@app.get("/metrics")
async def metrics():
"""Prometheus指标端点"""
return PlainTextResponse(generate_latest())
总结:从入门到实战的完整路径
通过本文的学习,你已经掌握了FastAPI的核心技能:
- 异步编程基础:理解async/await的工作机制
- 数据验证:使用Pydantic模型保证数据安全
- 依赖注入:构建松耦合的可维护代码
- 项目架构:设计企业级应用目录结构
- 完整实战:开发待办事项API系统
- 部署优化:配置生产环境和性能监控
下一步学习建议:
- 深入学习数据库:掌握SQLAlchemy 2.0的异步操作
- 探索高级特性:学习WebSocket、后台任务、自定义中间件
- 微服务架构:了解FastAPI在微服务中的应用
- 云原生部署:掌握Kubernetes和Docker Swarm
- 性能调优:学习APM工具和性能测试方法
FastAPI作为2025年Python后端的主流选择,不仅性能优异,而且开发体验极佳。现在就开始动手实践吧,用异步编程构建你的高性能API服务!
记住:最好的学习方式就是动手编码。从今天起,把你学到的每个知识点都通过代码实现,30天后,你将成为FastAPI的熟练开发者!