1. FastAPI 初识:为什么选择这个现代API框架
第一次接触FastAPI是在2019年,当时正在为一个物联网项目寻找高性能的Python API框架。相比Flask和Django REST Framework,FastAPI的异步支持和自动文档生成让我眼前一亮。这个由Sebastián Ramírez开发的框架,如今已成为Python领域构建API的首选工具之一。
FastAPI的核心优势在于:
- 极快的性能:基于Starlette和Pydantic,性能接近Node.js和Go
- 直观的编码体验:类型提示(Type Hints)让代码更健壮
- 自动交互文档:开箱即用的Swagger UI和ReDoc支持
- 异步友好:原生支持async/await语法
实际测试中,一个简单的GET接口在i7-10700K上能达到每秒15000+请求,而Flask同样配置下只有约3000请求/秒
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 开发环境准备与项目初始化
2.1 基础环境配置
建议使用Python 3.7+版本,我习惯用venv创建隔离环境:
bash复制python -m venv fastapi-env
source fastapi-env/bin/activate # Linux/Mac
fastapi-env\Scripts\activate # Windows
安装核心依赖:
bash复制pip install fastapi uvicorn[standard]
2.2 最小化应用结构
创建main.py作为入口文件:
python复制from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello World"}
启动开发服务器:
bash复制uvicorn main:app --reload
访问http://127.0.0.1:8000即可看到JSON响应,http://127.0.0.1:8000/docs则是交互式文档。
3. 路由系统深度解析
3.1 基础路由配置
FastAPI的路由装饰器与Flask类似但更强大:
python复制@app.get("/items/")
async def read_items():
return [{"item_id": "Foo"}]
@app.post("/items/")
async def create_item():
return {"status": "created"}
支持所有HTTP方法:@app.get(), @app.post(), @app.put(), @app.delete()等。
3.2 路径参数与类型转换
路径参数可以直接在路由中声明并自动转换类型:
python复制@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
如果访问/items/foo会得到自动的错误响应:
json复制{
"detail": [
{
"loc": ["path", "item_id"],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
3.3 路由顺序的重要性
FastAPI按声明顺序匹配路由,这个特性在包含相似路径时需要特别注意:
python复制@app.get("/users/me")
async def read_user_me():
return {"user_id": "current user"}
@app.get("/users/{user_id}")
async def read_user(user_id: str):
return {"user_id": user_id}
如果把这两个路由顺序颠倒,访问/users/me会永远匹配到/users/{user_id}路由。
4. 请求参数处理实战
4.1 查询参数
非路径参数自动识别为查询参数:
python复制from typing import Optional
@app.get("/items/")
async def read_items(q: Optional[str] = None):
if q:
return {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}], "q": q}
return {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
访问/items/?q=search时,q会被自动解析。
4.2 请求体与Pydantic模型
用Pydantic模型定义复杂请求体:
python复制from pydantic import BaseModel
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
tax: Optional[float] = None
@app.post("/items/")
async def create_item(item: Item):
item_dict = item.dict()
if item.tax:
total = item.price + item.tax
item_dict.update({"total": total})
return item_dict
发送POST请求时,FastAPI会自动:
- 读取请求体为JSON
- 转换为指定类型
- 验证数据
- 文档中生成对应JSON Schema
4.3 表单与文件上传
处理表单数据需要额外安装:
bash复制pip install python-multipart
然后可以处理文件上传:
python复制from fastapi import UploadFile, File
@app.post("/upload/")
async def upload_file(file: UploadFile = File(...)):
return {
"filename": file.filename,
"content_type": file.content_type
}
5. 响应模型与状态码
5.1 响应模型控制
用response_model参数控制输出数据结构:
python复制@app.post("/items/", response_model=Item)
async def create_item(item: Item):
return item
即使返回多余字段也会被自动过滤,这在API版本迭代时特别有用。
5.2 自定义状态码
通过status_code参数设置:
python复制from fastapi import status
@app.post("/items/", status_code=status.HTTP_201_CREATED)
async def create_item(item: Item):
return item
常用状态码常量都在fastapi.status中定义。
5.3 响应头与Cookie
返回自定义响应:
python复制from fastapi import Response
@app.get("/custom/")
async def custom_response(response: Response):
response.headers["X-Custom"] = "CustomValue"
response.set_cookie("test", "value")
return {"message": "check headers"}
6. 错误处理最佳实践
6.1 HTTPException基础用法
抛出标准错误响应:
python复制from fastapi import HTTPException
@app.get("/items/{item_id}")
async def read_item(item_id: str):
if item_id not in items:
raise HTTPException(
status_code=404,
detail="Item not found",
headers={"X-Error": "Item missing"}
)
return {"item": items[item_id]}
6.2 自定义异常处理器
统一处理特定异常:
python复制from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
class UnicornException(Exception):
def __init__(self, name: str):
self.name = name
app = FastAPI()
@app.exception_handler(UnicornException)
async def unicorn_exception_handler(request: Request, exc: UnicornException):
return JSONResponse(
status_code=418,
content={"message": f"Oops! {exc.name} did something wrong..."},
)
@app.get("/unicorns/{name}")
async def read_unicorn(name: str):
if name == "yolo":
raise UnicornException(name=name)
return {"unicorn_name": name}
7. 依赖注入系统
7.1 创建可复用依赖
python复制from fastapi import Depends
async def common_parameters(q: Optional[str] = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}
@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
return commons
7.2 类作为依赖项
python复制class Pagination:
def __init__(self, skip: int = 0, limit: int = 100):
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(pagination: Pagination = Depends()):
return {"skip": pagination.skip, "limit": pagination.limit}
8. 数据库集成实战
8.1 SQLAlchemy集成
安装依赖:
bash复制pip install sqlalchemy databases[postgresql]
配置数据库连接:
python复制from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
8.2 创建模型与CRUD操作
定义用户模型:
python复制from sqlalchemy import Column, Integer, String
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
email = Column(String, unique=True, index=True)
hashed_password = Column(String)
创建依赖项获取数据库会话:
python复制from fastapi import Depends
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.post("/users/")
async def create_user(user: UserCreate, db: Session = Depends(get_db)):
db_user = User(email=user.email, hashed_password=fake_hash(user.password))
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
9. 安全与认证
9.1 OAuth2密码流
安装安全依赖:
bash复制pip install python-jose[cryptography] passlib[bcrypt]
实现密码哈希:
python复制from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def verify_password(plain_password: str, hashed_password: str):
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password: str):
return pwd_context.hash(password)
9.2 JWT令牌生成
python复制from datetime import datetime, timedelta
from jose import jwt
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
def create_access_token(data: dict):
to_encode = data.copy()
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
10. 部署与性能优化
10.1 生产环境部署
推荐使用Gunicorn管理Uvicorn worker:
bash复制pip install gunicorn
启动命令:
bash复制gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app
10.2 性能优化技巧
- 启用Gzip压缩:
python复制from fastapi.middleware.gzip import GZipMiddleware
app.add_middleware(GZipMiddleware, minimum_size=1000)
- 使用
lru_cache缓存重复计算:
python复制from functools import lru_cache
@lru_cache()
def get_settings():
return Settings()
- 异步数据库驱动:选择支持async/await的驱动如
asyncpg
11. 常见问题排查
11.1 422 Unprocessable Entity错误
通常由请求体验证失败引起,检查:
- 请求头
Content-Type: application/json是否正确 - JSON字段是否与Pydantic模型匹配
- 是否缺少必填字段
11.2 数据库会话管理
常见错误模式:
python复制# 错误:在依赖项外创建会话
db = SessionLocal()
items = db.query(Item).all()
db.close() # 可能提前关闭
# 正确:使用依赖注入系统
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
11.3 跨域问题(CORS)
添加中间件解决:
python复制from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
12. 项目结构建议
中型项目推荐结构:
code复制.
├── app/
│ ├── __init__.py
│ ├── main.py
│ ├── api/
│ │ ├── __init__.py
│ │ ├── v1/
│ │ │ ├── __init__.py
│ │ │ ├── endpoints/
│ │ │ │ ├── items.py
│ │ │ │ └── users.py
│ │ │ └── routers.py
│ ├── core/
│ │ ├── config.py
│ │ └── security.py
│ ├── db/
│ │ ├── models.py
│ │ └── session.py
│ └── schemas/
│ └── items.py
├── tests/
│ ├── test_items.py
│ └── test_users.py
└── requirements.txt
路由组织示例:
python复制# api/v1/routers.py
from fastapi import APIRouter
router = APIRouter()
router.include_router(items.router, prefix="/items", tags=["items"])
router.include_router(users.router, prefix="/users", tags=["users"])
# main.py
from app.api.v1 import routers
app.include_router(routers.router, prefix="/api/v1")
13. 测试策略
13.1 单元测试示例
使用TestClient测试端点:
python复制from fastapi.testclient import TestClient
client = TestClient(app)
def test_read_item():
response = client.get("/items/42")
assert response.status_code == 200
assert response.json() == {"item_id": 42}
13.2 测试数据库操作
创建测试数据库会话:
python复制import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
@pytest.fixture
def test_db():
engine = create_engine("sqlite:///./test.db")
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base.metadata.create_all(bind=engine)
db = TestingSessionLocal()
try:
yield db
finally:
db.close()
Base.metadata.drop_all(bind=engine)
14. 进阶技巧
14.1 后台任务
对于耗时操作使用后台任务:
python复制from fastapi import BackgroundTasks
def write_log(message: str):
with open("log.txt", "a") as f:
f.write(message)
@app.post("/send-notification/{email}")
async def send_notification(
email: str, background_tasks: BackgroundTasks
):
background_tasks.add_task(write_log, f"email to {email}")
return {"message": "Notification sent"}
14.2 WebSocket支持
实时通信实现:
python复制from fastapi import WebSocket
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
while True:
data = await websocket.receive_text()
await websocket.send_text(f"Echo: {data}")
15. 监控与日志
15.1 结构化日志配置
python复制import logging
from fastapi.logger import logger
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[logging.StreamHandler(), logging.FileHandler("app.log")]
)
@app.get("/")
async def root():
logger.info("Root endpoint accessed")
return {"message": "Hello World"}
15.2 Prometheus监控
安装集成:
bash复制pip install prometheus-fastapi-instrumentator
配置中间件:
python复制from prometheus_fastapi_instrumentator import Instrumentator
Instrumentator().instrument(app).expose(app)
16. 实际项目经验分享
在电商API项目中遇到的几个关键问题及解决方案:
- N+1查询问题:使用SQLAlchemy的
selectinload预加载关联数据
python复制from sqlalchemy.orm import selectinload
items = db.query(Item).options(selectinload(Item.owner)).all()
- 分页性能:使用游标分页而非OFFSET/LIMIT
python复制@app.get("/items/")
async def list_items(
cursor: Optional[str] = None,
limit: int = 100,
db: Session = Depends(get_db)
):
query = db.query(Item).order_by(Item.id)
if cursor:
query = query.filter(Item.id > cursor)
items = query.limit(limit).all()
return {
"items": items,
"next_cursor": items[-1].id if items else None
}
- 批量导入优化:使用SQLAlchemy Core的bulk_insert_mappings
python复制from sqlalchemy import insert
def bulk_create_items(items: List[ItemCreate], db: Session):
stmt = insert(Item).values([item.dict() for item in items])
db.execute(stmt)
db.commit()
17. 生态工具推荐
- FastAPI Users:预构建的用户管理系统
bash复制pip install fastapi-users
- FastAPI Cache:响应缓存
bash复制pip install fastapi-cache2
- FastAPI Limiter:速率限制
bash复制pip install fastapi-limiter
- FastAPI Mail:邮件发送
bash复制pip install fastapi-mail
- FastAPI Pagination:标准化分页
bash复制pip install fastapi-pagination
18. 版本控制策略
18.1 URL路径版本控制
python复制from fastapi import APIRouter
router_v1 = APIRouter(prefix="/v1")
router_v2 = APIRouter(prefix="/v2")
@router_v1.get("/items/")
async def read_items_v1():
return {"version": "v1"}
@router_v2.get("/items/")
async def read_items_v2():
return {"version": "v2"}
app.include_router(router_v1)
app.include_router(router_v2)
18.2 请求头版本控制
python复制from fastapi import Header, APIRouter
router = APIRouter()
@router.get("/items/")
async def read_items(api_version: str = Header("1")):
if api_version == "2":
return {"version": "v2"}
return {"version": "v1"}
19. 文档定制技巧
19.1 自定义Swagger UI
修改默认文档URL:
python复制app = FastAPI(docs_url="/api/docs", redoc_url="/api/redoc")
添加标签和描述:
python复制@app.post(
"/items/",
tags=["Items"],
summary="Create an item",
description="Create an item with all the information",
response_description="The created item"
)
async def create_item(item: Item):
return item
19.2 添加Markdown文档
python复制app = FastAPI(
title="My API",
description="""
## My Awesome API
This API does fantastic things:
- Feature 1
- Feature 2
""",
version="0.1.0"
)
20. 微服务通信
20.1 使用HTTPX调用其他服务
python复制import httpx
@app.get("/external-data")
async def get_external_data():
async with httpx.AsyncClient() as client:
response = await client.get("https://api.example.com/data")
return response.json()
20.2 事件发布/订阅模式
使用Redis作为消息代理:
python复制from redis import asyncio as aioredis
@app.on_event("startup")
async def startup():
app.state.redis = await aioredis.from_url("redis://localhost")
@app.post("/publish/")
async def publish(channel: str, message: str):
await app.state.redis.publish(channel, message)
return {"status": "ok"}
@app.websocket("/subscribe/{channel}")
async def subscribe(websocket: WebSocket, channel: str):
await websocket.accept()
pubsub = app.state.redis.pubsub()
await pubsub.subscribe(channel)
async for message in pubsub.listen():
await websocket.send_text(message["data"].decode())
