python Web开发从入门到精通(二十一)FastAPI快速入门 - 体验异步编程的魅力

2 阅读1分钟

摘要钩子:还在为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

现在打开浏览器访问:

  1. API文档http://localhost:8000/docs(Swagger UI)
  2. 接口测试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": "张三"}

关键区别

  1. 同步函数:使用def定义,遇到IO操作(数据库、网络请求)时会阻塞整个线程
  2. 异步函数:使用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的优势

  1. 自动验证:类型、长度、正则、自定义验证器
  2. 自动文档:字段描述、示例值自动显示在Swagger中
  3. 自动转换:JSON ↔ Python对象双向转换
  4. 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
    }

参数优先级规则

  1. 路径参数:URL路径中的部分,如/items/{item_id}
  2. 查询参数:URL中?后面的部分,如?category=electronics
  3. 请求体:POST/PUT请求中的JSON数据
  4. 表单数据: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

依赖注入的优势

  1. 代码复用:一次定义,多处使用
  2. 易于测试:可以轻松模拟依赖
  3. 关注点分离:业务逻辑更清晰
  4. 类型安全:完整的类型提示

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            # 项目说明

这种模块化的项目结构让代码更易维护和扩展。下面是实际的项目架构示意图:

核心文件示例

  1. 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"}

  2. 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()

  3. 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 项目需求分析

我们要开发一个简单的待办事项管理系统,包含以下功能:

  1. 用户注册、登录、JWT认证
  2. 待办事项的增删改查
  3. 待办事项的状态管理(待完成/已完成)
  4. 数据验证和权限控制
  5. 自动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 运行与测试

  1. 启动应用

    uvicorn app.main:app --reload --host 0.0.0.0 --port 8000

  2. 访问文档

  • Swagger UI: http://localhost:8000/docs
  • ReDoc: http://localhost:8000/redoc
  1. 测试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的核心技能:

  1. 异步编程基础:理解async/await的工作机制
  2. 数据验证:使用Pydantic模型保证数据安全
  3. 依赖注入:构建松耦合的可维护代码
  4. 项目架构:设计企业级应用目录结构
  5. 完整实战:开发待办事项API系统
  6. 部署优化:配置生产环境和性能监控

下一步学习建议

  1. 深入学习数据库:掌握SQLAlchemy 2.0的异步操作
  2. 探索高级特性:学习WebSocket、后台任务、自定义中间件
  3. 微服务架构:了解FastAPI在微服务中的应用
  4. 云原生部署:掌握Kubernetes和Docker Swarm
  5. 性能调优:学习APM工具和性能测试方法

FastAPI作为2025年Python后端的主流选择,不仅性能优异,而且开发体验极佳。现在就开始动手实践吧,用异步编程构建你的高性能API服务!

记住:最好的学习方式就是动手编码。从今天起,把你学到的每个知识点都通过代码实现,30天后,你将成为FastAPI的熟练开发者!