本文为原创实战教程,涵盖 FastAPI 核心特性、路由设计、数据验证、数据库集成、认证授权、测试部署全流程,4000+字助你快速掌握现代 Python Web 开发利器。
在 Python Web 框架领域,Flask 和 Django 长期占据主导地位。但 FastAPI 自 2018 年发布以来迅速崛起,其魅力在于:
极致的性能:基于 Starlette(异步 Web 框架)和 Pydantic(数据验证),性能媲美 Go 和 Node.js
开发效率翻倍:自动生成 OpenAPI 文档、代码补全支持、直观的语法
类型提示革命:深度整合 Python 类型提示,减少 Bug 提高可读性
异步支持:原生支持 async/await
,轻松处理高并发 I/O 操作
强大的数据验证:Pydantic 模型自动验证请求/响应数据
# 安装核心库 (Python 3.7+)
pip install fastapi uvicorn[standard]
创建 main.py
:
from fastapi import FastAPI
app = FastAPI(
title="企业数据接口平台",
description="提供统一数据服务API",
version="1.0.0"
)
@app.get("/")
async def read_root():
return {"message": "欢迎使用数据服务平台"}
@app.get("/items/{item_id}")
def read_item(item_id: int, query_param: str = None):
return {"item_id": item_id, "query_param": query_param}
启动服务器:
uvicorn main:app --reload --port 8000
访问以下地址:
API 文档:http://localhost:8000/docs
接口测试:http://localhost:8000/items/42?query_param=test
from enum import Enum
class ItemCategory(str, Enum):
ELECTRONICS = "electronics"
BOOKS = "books"
CLOTHING = "clothing"
@app.get("/category/{category_name}")
async def get_category(category_name: ItemCategory):
if category_name == ItemCategory.ELECTRONICS:
return {"category": "电子", "promotion": "满1000减200"}
return {"category": category_name.value}
from typing import Optional, List
@app.get("/search/")
async def product_search(
keyword: str,
category: Optional[str] = None,
min_price: float = 0,
max_price: float = 10000,
tags: List[str] = Query([])
):
# 实际项目中这里连接数据库
return {
"keyword": keyword,
"filters": {
"category": category,
"price_range": [min_price, max_price],
"tags": tags
}
}
from fastapi import HTTPException
ITEMS_DB = {}
@app.post("/items/")
async def create_item(item: dict):
item_id = len(ITEMS_DB) + 1
ITEMS_DB[item_id] = item
return {"id": item_id, **item}
@app.put("/items/{item_id}")
async def update_item(item_id: int, item_update: dict):
if item_id not in ITEMS_DB:
raise HTTPException(status_code=404, detail="商品不存在")
ITEMS_DB[item_id].update(item_update)
return {"status": "更新成功", "item": ITEMS_DB[item_id]}
@app.delete("/items/{item_id}")
async def delete_item(item_id: int):
if item_id not in ITEMS_DB:
raise HTTPException(status_code=404, detail="商品不存在")
del ITEMS_DB[item_id]
return {"status": "删除成功"}
from pydantic import BaseModel, EmailStr, Field
from datetime import datetime
class UserCreate(BaseModel):
username: str = Field(..., min_length=3, example="john_doe")
email: EmailStr
password: str = Field(..., min_length=8, regex=r"^(?=.*[A-Za-z])(?=.*\d).{8,}$")
class UserResponse(BaseModel):
id: int
username: str
email: str
created_at: datetime
is_active: bool = True
class Config:
orm_mode = True # 支持ORM对象转换
from fastapi import status
users_db = []
@app.post("/users/", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
async def create_user(user: UserCreate):
# 密码哈希处理(实际项目使用passlib)
hashed_password = f"hashed_{user.password}"
# 创建用户记录
db_user = {
"id": len(users_db) + 1,
"username": user.username,
"email": user.email,
"password_hash": hashed_password,
"created_at": datetime.now(),
"is_active": True
}
users_db.append(db_user)
return db_user
pip install sqlmodel
from sqlmodel import SQLModel, Field, Session, create_engine
from typing import Optional
class Product(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
description: Optional[str] = Field(default=None)
price: float = Field(gt=0)
category: str
in_stock: bool = True
# 数据库连接配置
DATABASE_URL = "sqlite:///./app.db"
engine = create_engine(DATABASE_URL, echo=True)
# 创建表
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
@app.on_event("startup")
def on_startup():
create_db_and_tables()
@app.post("/products/", response_model=Product)
async def create_product(product: Product):
with Session(engine) as session:
session.add(product)
session.commit()
session.refresh(product)
return product
@app.get("/products/{product_id}", response_model=Product)
async def read_product(product_id: int):
with Session(engine) as session:
product = session.get(Product, product_id)
if not product:
raise HTTPException(status_code=404, detail="产品未找到")
return product
@app.get("/products/", response_model=list[Product])
async def read_products(category: Optional[str] = None, min_price: float = 0):
with Session(engine) as session:
query = session.query(Product)
if category:
query = query.filter(Product.category == category)
if min_price > 0:
query = query.filter(Product.price >= min_price)
return query.all()
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from passlib.context import CryptContext
# 密码哈希配置
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# OAuth2 配置
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
# 用户认证函数
def authenticate_user(username: str, password: str):
# 实际项目从数据库验证
if username == "admin" and pwd_context.verify(password, pwd_context.hash("secret")):
return {"username": username}
return None
@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
user = authenticate_user(form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=401,
detail="用户名或密码错误",
headers={"WWW-Authenticate": "Bearer"}
)
# 实际项目生成JWT
access_token = "generated_jwt_token_here"
return {"access_token": access_token, "token_type": "bearer"}
@app.get("/secure-data/")
async def get_secure_data(token: str = Depends(oauth2_scheme)):
# 实际项目验证JWT
if token != "generated_jwt_token_here":
raise HTTPException(status_code=401, detail="无效凭证")
return {"data": "敏感数据"}
from fastapi import Depends, Security
from fastapi.security import SecurityScopes
class UserRole(str, Enum):
ADMIN = "admin"
EDITOR = "editor"
VIEWER = "viewer"
def get_current_user(security_scopes: SecurityScopes, token: str = Depends(oauth2_scheme)):
# 实际项目解析JWT获取用户信息
user_roles = [UserRole.ADMIN] # 模拟角色
# 检查权限
for scope in security_scopes.scopes:
if scope not in user_roles:
raise HTTPException(
status_code=403,
detail="权限不足",
headers={"scopes": security_scopes.scope_str}
)
return {"username": "admin", "roles": user_roles}
@app.get("/admin/dashboard", dependencies=[Security(get_current_user, scopes=["admin"])])
async def admin_dashboard():
return {"message": "管理员控制台"}
import time
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
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["Content-Security-Policy"] = "default-src 'self'"
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
return response
from fastapi import BackgroundTasks
def send_notification(email: str, message: str):
# 模拟发送邮件
print(f"发送邮件到 {email}: {message}")
time.sleep(2) # 模拟耗时操作
@app.post("/orders/")
async def create_order(
background_tasks: BackgroundTasks,
user_email: str,
order_data: dict
):
# 创建订单逻辑...
order_id = 1001
# 添加后台任务
background_tasks.add_task(
send_notification,
user_email,
f"订单 #{order_id} 创建成功"
)
return {"order_id": order_id, "status": "已创建"}
from fastapi import WebSocket
active_connections = []
@app.websocket("/ws/notifications")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
active_connections.append(websocket)
try:
while True:
data = await websocket.receive_text()
# 广播消息给所有客户端
for connection in active_connections:
await connection.send_text(f"收到消息: {data}")
except WebSocketDisconnect:
active_connections.remove(websocket)
pip install pytest httpx sqlalchemy pytest-asyncio
# test_main.py
import pytest
from fastapi.testclient import TestClient
from main import app
client = TestClient(app)
def test_read_root():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"message": "欢迎使用数据服务平台"}
def test_create_product():
test_product = {
"name": "测试产品",
"description": "测试描述",
"price": 99.99,
"category": "测试类目"
}
response = client.post("/products/", json=test_product)
assert response.status_code == 200
data = response.json()
assert data["name"] == test_product["name"]
assert "id" in data
@pytest.mark.asyncio
async def test_websocket():
with client.websocket_connect("/ws/notifications") as websocket:
websocket.send_text("测试消息")
data = websocket.receive_text()
assert data == "收到消息: 测试消息"
uvicorn main:app \
--host 0.0.0.0 \
--port 8000 \
--workers 4 \
--timeout-keep-alive 60 \
--no-access-log
# Dockerfile
FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]
# /etc/nginx/sites-available/fastapi-app
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# WebSocket 支持
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# 启用Gzip压缩
gzip on;
gzip_types application/json;
}
数据库连接池配置
from sqlalchemy.pool import QueuePool
engine = create_engine(
DATABASE_URL,
poolclass=QueuePool,
pool_size=10,
max_overflow=20,
pool_timeout=30
)
2. 响应缓存策略
from fastapi_cache import FastAPICache
from fastapi_cache.backends.redis import RedisBackend
from fastapi_cache.decorator import cache
@app.on_event("startup")
async def startup():
FastAPICache.init(RedisBackend("redis://localhost"), prefix="fastapi-cache")
@app.get("/products/{product_id}")
@cache(expire=300) # 缓存5分钟
async def get_product(product_id: int):
# 数据库查询
return product
3. 异步数据库驱动
# 使用asyncpg连接PostgreSQL
DATABASE_URL = "postgresql+asyncpg://user:password@localhost/dbname"
engine = create_async_engine(DATABASE_URL)
问题1:依赖项复杂导致代码臃肿
# 使用依赖注入解耦
def get_db():
with Session(engine) as session:
yield session
def get_current_user(db: Session = Depends(get_db)):
# 从数据库获取用户
...
@app.get("/user/profile")
async def user_profile(user = Depends(get_current_user)):
return user
问题2:处理大文件上传
from fastapi import UploadFile, File
@app.post("/upload/")
async def upload_large_file(file: UploadFile = File(...)):
# 流式处理文件
with open(f"uploads/{file.filename}", "wb") as buffer:
while chunk := await file.read(1024 * 1024): # 每次读取1MB
buffer.write(chunk)
return {"filename": file.filename}
问题3:处理跨域请求
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["https://example.com", "http://localhost:3000"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
性能基准:比 Flask 快 3 倍以上,接近 Node.js/Go 的性能
开发体验:自动文档、类型提示、编辑器自动补全大幅提升效率
现代化特性:原生异步支持、WebSocket、后台任务
生态系统:兼容 Starlette 中间件、Pydantic 数据验证、SQLAlchemy 集成
生产就绪:完善的测试支持、容器化部署方案、监控集成
实战建议:
大型项目使用
APIRouter
模块化组织代码生产环境使用 Gunicorn + Uvicorn Worker
重要接口实现速率限制(如
slowapi
)使用
Sentry
进行错误监控自动化 API 文档更新流程