2030 字
10 分钟

FastAPI 完全指南 2026:从零构建高性能异步 Python API 到 Docker 生产部署

FastAPI 是 2019 年发布、目前最受欢迎的 Python Web API 框架之一,在 GitHub Star 数上已超越 Flask 和 Django REST Framework。它的核心竞争力:原生异步支持 + 自动 OpenAPI 文档 + Pydantic 数据验证 三位一体,让 Python API 开发效率大幅提升。

本文从零开始,完整覆盖 FastAPI 核心功能到生产部署的全链路。


一、安装与项目结构#

1.1 安装#

Terminal window
# 推荐使用 uv 管理项目
uv init my-api --lib
cd my-api
# 添加依赖
uv add fastapi uvicorn[standard] pydantic-settings
# 可选:数据库相关
uv add sqlalchemy[asyncio] asyncpg alembic
# 可选:认证相关
uv add python-jose[cryptography] passlib[bcrypt]
# 开发依赖
uv add --dev pytest pytest-asyncio httpx ruff mypy

1.2 推荐项目结构#

my-api/
├── pyproject.toml
├── alembic/ # 数据库迁移
│ └── versions/
├── src/
│ └── my_api/
│ ├── __init__.py
│ ├── main.py # FastAPI 应用入口
│ ├── config.py # 配置管理(pydantic-settings)
│ ├── database.py # 数据库连接
│ ├── models/ # SQLAlchemy ORM 模型
│ │ └── user.py
│ ├── schemas/ # Pydantic 请求/响应模型
│ │ └── user.py
│ ├── routers/ # 路由(按资源拆分)
│ │ ├── users.py
│ │ └── auth.py
│ ├── services/ # 业务逻辑层
│ │ └── user.py
│ └── deps.py # 依赖注入(认证、数据库 Session 等)
└── tests/
└── test_users.py

二、核心功能速览#

2.1 最小应用#

src/my_api/main.py
from fastapi import FastAPI
app = FastAPI(
title="My API",
description="API 文档说明",
version="1.0.0",
docs_url="/docs", # Swagger UI 路径
redoc_url="/redoc", # ReDoc 路径
)
@app.get("/")
async def root():
return {"message": "Hello, FastAPI!"}
@app.get("/health")
async def health():
return {"status": "ok"}
Terminal window
# 启动开发服务器(热重载)
uvicorn src.my_api.main:app --reload --host 0.0.0.0 --port 8000
# 或用 uv
uv run uvicorn src.my_api.main:app --reload
# 访问自动生成的 Swagger UI
open http://localhost:8000/docs

2.2 路径参数 / 查询参数 / 请求体#

from fastapi import FastAPI, Path, Query, Body
from pydantic import BaseModel, Field
from typing import Annotated
app = FastAPI()
# ── 路径参数 ──────────────────────────────────────────────────
@app.get("/users/{user_id}")
async def get_user(
user_id: Annotated[int, Path(title="用户ID", ge=1)] # ge=1:必须 >= 1
):
return {"user_id": user_id}
# ── 查询参数 ──────────────────────────────────────────────────
@app.get("/users")
async def list_users(
page: Annotated[int, Query(ge=1, description="页码")] = 1,
per_page: Annotated[int, Query(ge=1, le=100)] = 20,
search: str | None = None, # 可选查询参数
active: bool = True,
):
return {
"page": page,
"per_page": per_page,
"search": search,
"active": active,
}
# ── 请求体(Pydantic 模型)──────────────────────────────────────
class UserCreate(BaseModel):
username: str = Field(..., min_length=3, max_length=50, pattern=r'^[a-zA-Z0-9_]+$')
email: str = Field(..., description="用户邮箱")
age: int = Field(..., ge=0, le=150)
bio: str | None = Field(default=None, max_length=500)
class UserResponse(BaseModel):
id: int
username: str
email: str
model_config = {"from_attributes": True} # 允许从 ORM 对象创建
@app.post("/users", response_model=UserResponse, status_code=201)
async def create_user(user: UserCreate):
# Pydantic 自动验证 user 数据,验证失败自动返回 422
return {"id": 1, **user.model_dump()}

2.3 配置管理(pydantic-settings)#

src/my_api/config.py
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
# 优先从环境变量读取,其次从 .env 文件
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
case_sensitive=False,
)
# 数据库
database_url: str = "postgresql+asyncpg://user:pass@localhost/mydb"
# JWT 认证
secret_key: str = "change-this-in-production"
algorithm: str = "HS256"
access_token_expire_minutes: int = 30
# 应用
debug: bool = False
app_name: str = "My API"
api_prefix: str = "/api/v1"
# 全局单例
settings = Settings()

三、依赖注入(Depends)#

FastAPI 的依赖注入系统是其最强大的特性之一:

src/my_api/deps.py
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
from .config import settings
from .database import AsyncSessionLocal
# ── 数据库 Session 依赖 ───────────────────────────────────────
async def get_db():
async with AsyncSessionLocal() as session:
try:
yield session
await session.commit()
except Exception:
await session.rollback()
raise
# ── JWT 认证依赖 ──────────────────────────────────────────────
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login")
async def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="无效的认证凭证",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, settings.secret_key, algorithms=[settings.algorithm])
user_id: int | None = payload.get("sub")
if user_id is None:
raise credentials_exception
except JWTError:
raise credentials_exception
return user_id
# ── 在路由中使用依赖 ──────────────────────────────────────────
from fastapi import APIRouter
from sqlalchemy.ext.asyncio import AsyncSession
router = APIRouter()
@router.get("/me")
async def get_me(
current_user_id: int = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
# current_user_id 和 db 自动注入
return {"user_id": current_user_id}

四、数据库集成(SQLAlchemy 2.0 异步)#

src/my_api/database.py
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
from sqlalchemy.orm import DeclarativeBase
from .config import settings
engine = create_async_engine(
settings.database_url,
echo=settings.debug, # debug=True 时打印 SQL 语句
pool_size=10,
max_overflow=20,
)
AsyncSessionLocal = async_sessionmaker(
engine,
class_=AsyncSession,
expire_on_commit=False,
)
class Base(DeclarativeBase):
pass
src/my_api/models/user.py
from datetime import datetime
from sqlalchemy import String, Boolean, DateTime, func
from sqlalchemy.orm import Mapped, mapped_column
from ..database import Base
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True, index=True)
username: Mapped[str] = mapped_column(String(50), unique=True, index=True)
email: Mapped[str] = mapped_column(String(255), unique=True, index=True)
hashed_password: Mapped[str] = mapped_column(String(255))
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
updated_at: Mapped[datetime] = mapped_column(
DateTime, server_default=func.now(), onupdate=func.now()
)
src/my_api/services/user.py
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from ..models.user import User
from ..schemas.user import UserCreate
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
class UserService:
def __init__(self, db: AsyncSession):
self.db = db
async def get_by_id(self, user_id: int) -> User | None:
result = await self.db.execute(select(User).where(User.id == user_id))
return result.scalar_one_or_none()
async def get_by_email(self, email: str) -> User | None:
result = await self.db.execute(select(User).where(User.email == email))
return result.scalar_one_or_none()
async def create(self, data: UserCreate) -> User:
user = User(
username=data.username,
email=data.email,
hashed_password=pwd_context.hash(data.password),
)
self.db.add(user)
await self.db.flush() # 获取自动生成的 ID,不提交事务
await self.db.refresh(user)
return user
def verify_password(self, plain: str, hashed: str) -> bool:
return pwd_context.verify(plain, hashed)

五、JWT 认证完整实现#

src/my_api/routers/auth.py
from datetime import datetime, timedelta, timezone
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from jose import jwt
from sqlalchemy.ext.asyncio import AsyncSession
from ..config import settings
from ..deps import get_db
from ..services.user import UserService
router = APIRouter(prefix="/auth", tags=["认证"])
def create_access_token(data: dict, expires_delta: timedelta | None = None) -> str:
to_encode = data.copy()
expire = datetime.now(timezone.utc) + (expires_delta or timedelta(minutes=30))
to_encode.update({"exp": expire})
return jwt.encode(to_encode, settings.secret_key, algorithm=settings.algorithm)
@router.post("/login")
async def login(
form_data: OAuth2PasswordRequestForm = Depends(),
db: AsyncSession = Depends(get_db),
):
service = UserService(db)
user = await service.get_by_email(form_data.username)
if not user or not service.verify_password(form_data.password, user.hashed_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="邮箱或密码错误",
)
access_token = create_access_token(
data={"sub": str(user.id)},
expires_delta=timedelta(minutes=settings.access_token_expire_minutes),
)
return {
"access_token": access_token,
"token_type": "bearer",
}

六、中间件与错误处理#

src/my_api/main.py
import time
import uuid
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from .config import settings
from .routers import users, auth
app = FastAPI(title=settings.app_name)
# ── CORS 跨域中间件 ───────────────────────────────────────────
app.add_middleware(
CORSMiddleware,
allow_origins=["https://your-frontend.com"], # 生产环境写具体域名
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ── 请求 ID 和耗时日志中间件 ──────────────────────────────────
@app.middleware("http")
async def add_request_context(request: Request, call_next):
request_id = str(uuid.uuid4())[:8]
start = time.perf_counter()
response = await call_next(request)
duration = (time.perf_counter() - start) * 1000
response.headers["X-Request-ID"] = request_id
response.headers["X-Process-Time"] = f"{duration:.2f}ms"
return response
# ── 全局异常处理 ──────────────────────────────────────────────
@app.exception_handler(404)
async def not_found_handler(request: Request, exc):
return JSONResponse(status_code=404, content={"error": "资源不存在"})
@app.exception_handler(500)
async def server_error_handler(request: Request, exc):
return JSONResponse(status_code=500, content={"error": "服务器内部错误"})
# ── 注册路由 ──────────────────────────────────────────────────
app.include_router(auth.router, prefix=settings.api_prefix)
app.include_router(users.router, prefix=settings.api_prefix)

七、后台任务与 WebSocket#

from fastapi import BackgroundTasks
# ── 后台任务(轻量异步任务,如发送邮件)────────────────────────
def send_welcome_email(email: str):
"""在后台线程中执行(不阻塞响应)"""
import time
time.sleep(2) # 模拟发邮件的延迟
print(f"邮件已发送到 {email}")
@app.post("/users", status_code=201)
async def create_user(user: UserCreate, background_tasks: BackgroundTasks):
# 创建用户...
background_tasks.add_task(send_welcome_email, user.email)
return {"message": "用户创建成功,欢迎邮件发送中"}
# ── WebSocket ─────────────────────────────────────────────────
from fastapi import WebSocket
from fastapi.websockets import WebSocketDisconnect
@app.websocket("/ws/{client_id}")
async def websocket_endpoint(websocket: WebSocket, client_id: str):
await websocket.accept()
try:
while True:
data = await websocket.receive_text()
await websocket.send_text(f"[{client_id}] 收到: {data}")
except WebSocketDisconnect:
print(f"客户端 {client_id} 已断开")

八、测试(pytest + httpx)#

tests/conftest.py
import pytest
import pytest_asyncio
from httpx import AsyncClient, ASGITransport
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
from src.my_api.main import app
from src.my_api.database import Base
from src.my_api.deps import get_db
# 使用内存 SQLite 作为测试数据库
TEST_DATABASE_URL = "sqlite+aiosqlite:///:memory:"
@pytest_asyncio.fixture(scope="session")
async def db_engine():
engine = create_async_engine(TEST_DATABASE_URL, echo=False)
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
yield engine
await engine.dispose()
@pytest_asyncio.fixture
async def db_session(db_engine):
session_factory = async_sessionmaker(db_engine, class_=AsyncSession)
async with session_factory() as session:
yield session
await session.rollback() # 每个测试后回滚,保持隔离
@pytest_asyncio.fixture
async def client(db_session):
# 覆盖依赖注入,使用测试数据库
app.dependency_overrides[get_db] = lambda: db_session
async with AsyncClient(
transport=ASGITransport(app=app),
base_url="http://test",
) as c:
yield c
app.dependency_overrides.clear()
tests/test_users.py
import pytest
@pytest.mark.asyncio
async def test_create_user(client):
response = await client.post("/api/v1/users", json={
"username": "testuser",
"email": "test@example.com",
"password": "SecurePass123!",
"age": 25,
})
assert response.status_code == 201
data = response.json()
assert data["username"] == "testuser"
assert "password" not in data # 确保密码不在响应中
@pytest.mark.asyncio
async def test_login(client):
# 先创建用户
await client.post("/api/v1/users", json={
"username": "loginuser",
"email": "login@example.com",
"password": "TestPass123!",
"age": 30,
})
# 测试登录
response = await client.post(
"/api/v1/auth/login",
data={"username": "login@example.com", "password": "TestPass123!"},
)
assert response.status_code == 200
assert "access_token" in response.json()
@pytest.mark.asyncio
async def test_get_me_unauthorized(client):
response = await client.get("/api/v1/me")
assert response.status_code == 401
Terminal window
# 运行测试
uv run pytest tests/ -v --asyncio-mode=auto

九、Docker 生产部署#

9.1 Dockerfile(多阶段构建)#

FROM python:3.12-slim AS builder
WORKDIR /app
RUN pip install uv
COPY pyproject.toml uv.lock ./
RUN uv venv /opt/venv && uv sync --frozen --no-dev --python /opt/venv/bin/python
COPY src/ ./src/
FROM python:3.12-slim AS final
RUN useradd --no-create-home --shell /bin/false appuser
WORKDIR /app
COPY --from=builder /opt/venv /opt/venv
COPY --from=builder /app/src ./src
ENV PATH="/opt/venv/bin:$PATH"
USER appuser
EXPOSE 8000
CMD ["uvicorn", "src.my_api.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]

9.2 compose.yml(生产配置)#

services:
api:
build: .
restart: unless-stopped
environment:
DATABASE_URL: postgresql+asyncpg://appuser:${DB_PASSWORD}@db:5432/myapp
SECRET_KEY: ${SECRET_KEY}
DEBUG: "false"
depends_on:
db:
condition: service_healthy
networks: [backend, frontend]
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: myapp
POSTGRES_USER: appuser
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- db-data:/var/lib/postgresql/data
networks: [backend]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d myapp"]
interval: 10s
timeout: 5s
retries: 5
nginx:
image: nginx:1.27-alpine
restart: unless-stopped
ports: ["80:80", "443:443"]
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on: [api]
networks: [frontend]
volumes:
db-data:
networks:
frontend:
backend:
internal: true

相关文章

本文基于 FastAPI 0.111+ / Pydantic v2 / SQLAlchemy 2.0 编写,接口以官方文档为准:fastapi.tiangolo.com

FastAPI 完全指南 2026:从零构建高性能异步 Python API 到 Docker 生产部署
https://971918.xyz/posts/python-guide/python-fastapi-complete-guide/
作者
九所长
发布于
2026-07-20
许可协议
CC BY-NC-SA 4.0