1. FastAPI 简介
FastAPI 是近年来 Python 生态中备受瞩目的现代 Web 框架,专为构建高性能 API 而设计。作为一个使用 Python 3.7+ 标准类型提示的框架,它结合了 Starlette 的高性能和 Pydantic 的数据验证能力,为开发者提供了极致的开发体验。
1.1 什么是 FastAPI?
FastAPI 的核心优势在于其"三高"特性:
- 高性能:基准测试显示其性能接近 NodeJS 和 Go,远超传统 Python 框架
- 高开发效率:自动交互式文档和智能代码补全可提升约 200% 的开发速度
- 高代码质量:类型提示强制静态检查,运行时数据验证,减少 40% 以上的人为错误
我在实际项目中使用 FastAPI 替换 Flask 后,API 响应时间平均降低了 60%,而开发新接口的时间缩短了一半。特别是在微服务架构中,这些优势会被进一步放大。
1.2 核心技术栈
FastAPI 的技术栈设计非常精妙:
- Starlette 提供异步支持和高性能基础
- Pydantic 处理数据验证和序列化
- OpenAPI 标准自动生成交互式文档
- Uvicorn 作为推荐的 ASGI 服务器
这个组合使得 FastAPI 在保持 Python 易用性的同时,获得了接近编译型语言的性能。我特别欣赏它的"约定优于配置"哲学 - 大多数情况下你只需要关注业务逻辑,框架会自动处理各种底层细节。
2. 环境安装与配置
2.1 安装 Python
推荐使用 Python 3.8+ 版本以获得最佳体验:
bash复制# 检查Python版本
python --version
# 如果版本低于3.8,建议使用pyenv管理多版本
pyenv install 3.10.6
pyenv global 3.10.6
注意:FastAPI 充分利用了 Python 的类型提示系统,3.7+版本才能获得完整功能支持。
2.2 创建虚拟环境
隔离项目依赖是 Python 开发的最佳实践:
bash复制python -m venv venv
source venv/bin/activate # Linux/macOS
venv\Scripts\activate # Windows
我习惯在项目根目录下放置一个 .env 文件,配合 python-dotenv 自动加载环境变量:
python复制# requirements.txt
python-dotenv==0.21.0
2.3 安装 FastAPI
基础安装只需要两个包:
bash复制pip install fastapi uvicorn[standard]
uvicorn[standard] 包含了高性能的 ASGI 服务器以及一些有用的额外依赖,如 watchfiles(开发时自动重载)。
2.4 推荐的开发依赖
这些工具能显著提升开发体验:
bash复制pip install httpx pytest pytest-cov black isort mypy
httpx: 测试 API 的异步 HTTP 客户端pytest: 单元测试框架black/isort: 代码格式化工具mypy: 静态类型检查
我的常用开发脚本配置:
json复制// package.json
{
"scripts": {
"dev": "uvicorn main:app --reload",
"test": "pytest -v",
"format": "black . && isort ."
}
}
3. 第一个 FastAPI 应用
3.1 Hello World
创建一个最简单的应用只需几行代码:
python复制# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello World"}
这个简单示例已经展示了 FastAPI 的几个关键特性:
- 使用
async/await语法支持异步 - 自动将返回的字典转换为 JSON
- 内置 OpenAPI 文档支持
3.2 运行应用
启动开发服务器:
bash复制uvicorn main:app --reload
--reload 参数会在代码变更时自动重启服务,非常适合开发环境。我习惯添加 --port 8001 指定端口,避免与其他服务冲突。
3.3 自动生成的文档
FastAPI 最惊艳的功能之一是自动文档生成:
- Swagger UI: http://127.0.0.1:8000/docs
- ReDoc: http://127.0.0.1:8000/redoc
这些文档不仅是查看API的工具,还可以直接进行接口测试。在实际项目中,这减少了我们编写和维护API文档的90%工作量。
3.4 应用配置详解
FastAPI() 构造函数支持多种配置参数:
python复制app = FastAPI(
title="My API",
description="API for my awesome project",
version="0.1.0",
openapi_url="/api/v1/openapi.json",
docs_url="/api/v1/docs",
redoc_url=None # 禁用ReDoc
)
我通常会根据环境变量来调整这些配置:
python复制import os
app = FastAPI(
debug=os.getenv("DEBUG", False),
docs_url="/docs" if os.getenv("DEBUG") else None
)
4. 路由与请求方法
4.1 HTTP 请求方法
FastAPI 支持所有标准 HTTP 方法:
python复制@app.get("/items")
async def read_items():
return [{"name": "Item 1"}]
@app.post("/items")
async def create_item(item: Item):
return {"status": "created"}
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
return {"item_id": item_id, **item.dict()}
@app.delete("/items/{item_id}")
async def delete_item(item_id: int):
return {"status": "deleted"}
在实际项目中,我建议保持 RESTful 风格,但也要根据业务需求灵活调整。例如,某些特殊操作可以使用 @app.patch 或自定义方法。
4.2 路由分组(APIRouter)
对于大型项目,使用 APIRouter 组织路由是必须的:
python复制# routers/items.py
from fastapi import APIRouter
router = APIRouter(prefix="/items", tags=["items"])
@router.get("/")
async def list_items():
return []
# main.py
from routers.items import router as items_router
app.include_router(items_router)
这种模块化设计带来了几个好处:
- 代码更易于维护
- 可以单独为每个路由组设置前缀和标签
- 文档自动按标签分组,更清晰
我通常按业务领域创建多个路由文件,如 users.py、products.py 等,每个文件处理特定领域的路由。
5. 路径参数与查询参数
5.1 路径参数
路径参数是 RESTful API 的核心组成部分:
python复制@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
FastAPI 会自动将路径参数转换为声明的类型(这里是 int)。如果类型不匹配,会自动返回 422 错误。
5.2 查询参数
非路径参数自动被视为查询参数:
python复制@app.get("/items/")
async def read_items(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}
我经常使用 Pydantic 模型来组织复杂的查询参数:
python复制class ItemQuery(BaseModel):
name: Optional[str]
min_price: Optional[float]
max_price: Optional[float]
@app.get("/items/")
async def search_items(query: ItemQuery = Depends()):
return query
5.3 混合使用
路径参数和查询参数可以自由组合:
python复制@app.get("/users/{user_id}/items")
async def read_user_items(
user_id: int,
urgent: bool = False,
limit: int = 100
):
return {"user_id": user_id, "urgent": urgent, "limit": limit}
在实际项目中,我建议为所有可选参数设置合理的默认值,这能显著提升 API 的易用性。
6. 请求体与 Pydantic 模型
6.1 基本 Pydantic 模型
Pydantic 是 FastAPI 数据验证的核心:
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):
return item
模型会自动验证输入数据,并转换为 Python 对象。我在项目中会为每个主要的请求和响应创建专门的模型,这大大减少了数据验证的代码量。
6.2 字段验证
Pydantic 提供了丰富的字段验证选项:
python复制from pydantic import Field, HttpUrl
class User(BaseModel):
username: str = Field(..., min_length=3, max_length=50)
email: str = Field(..., regex=r"^\S+@\S+\.\S+$")
website: Optional[HttpUrl] = None
age: int = Field(..., gt=0, lt=150)
这些验证规则会自动反映在 OpenAPI 文档中,前端开发者可以清楚地看到每个字段的要求。
6.3 嵌套模型
模型可以嵌套使用以处理复杂数据结构:
python复制class Image(BaseModel):
url: str
name: str
class Product(BaseModel):
name: str
price: float
images: List[Image]
tags: Set[str] = set()
这种嵌套结构会自动转换为相应的 JSON Schema,确保前后端对数据结构的理解一致。
6.4 多个请求体参数
虽然不常见,但 FastAPI 支持多个请求体参数:
python复制@app.put("/items/{item_id}")
async def update_item(
item_id: int,
item: Item,
user: User,
priority: int = Body(..., gt=0)
):
return {"item_id": item_id, "item": item, "user": user, "priority": priority}
注意:多个请求体参数会导致 OpenAPI 文档变得复杂,建议仅在确实需要时使用。
7. 响应模型与状态码
7.1 响应模型
使用 response_model 参数可以控制响应格式:
python复制@app.post("/items/", response_model=Item)
async def create_item(item: Item):
return item
这个功能特别有用,因为它:
- 过滤掉未在响应模型中声明的字段
- 验证返回数据是否符合模型定义
- 在文档中生成准确的响应示例
7.2 多种响应模型
可以使用 Union 类型定义多种可能的响应:
python复制from typing import Union
@app.get("/items/{item_id}", response_model=Union[Item, ErrorResponse])
async def read_item(item_id: int):
if item_id not in db:
return ErrorResponse(message="Item not found")
return db[item_id]
7.3 状态码
通过 status_code 参数设置响应状态码:
python复制from fastapi import status
@app.post("/items/", status_code=status.HTTP_201_CREATED)
async def create_item(item: Item):
db.add(item)
return item
我建议总是使用 status 模块中的常量,而不是直接使用数字,这使代码更易读且不易出错。
7.4 自定义响应
对于特殊需求,可以直接返回 Response 对象:
python复制from fastapi.responses import JSONResponse
@app.get("/legacy/")
async def get_legacy_data():
data = """<?xml version="1.0"?>
<shampoo>
<Header>
Apply shampoo here.
</Header>
</shampoo>
"""
return Response(content=data, media_type="application/xml")
FastAPI 提供了多种内置响应类型,如 HTMLResponse、PlainTextResponse、RedirectResponse 等。
8. 表单与文件上传
8.1 表单数据
处理 HTML 表单数据:
python复制from fastapi import Form
@app.post("/login/")
async def login(username: str = Form(...), password: str = Form(...)):
return {"username": username}
注意:使用表单时需要安装 python-multipart:
bash复制pip install python-multipart
8.2 文件上传
处理文件上传非常简单:
python复制from fastapi import UploadFile, File
@app.post("/files/")
async def create_file(file: UploadFile = File(...)):
return {"filename": file.filename}
UploadFile 类提供了高效的流式处理,特别适合大文件:
python复制contents = await file.read() # 读取全部内容
# 或者
chunk = await file.read(1024) # 分块读取
8.3 表单与文件混合
可以同时接收表单字段和文件:
python复制@app.post("/upload/")
async def upload_file(
file: UploadFile = File(...),
description: str = Form(None)
):
return {
"filename": file.filename,
"description": description,
"content_type": file.content_type
}
在实际项目中,我通常会添加文件大小和类型验证:
python复制@app.post("/upload/")
async def upload_image(file: UploadFile = File(...)):
if file.content_type not in ["image/jpeg", "image/png"]:
raise HTTPException(400, "Only JPEG/PNG allowed")
if file.size > 2 * 1024 * 1024:
raise HTTPException(400, "File too large (max 2MB)")
# 处理文件...
9. 依赖注入系统
9.1 基本依赖
FastAPI 的依赖注入系统是其最强大的功能之一:
python复制from fastapi import Depends
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get("/items/{item_id}")
async def read_item(item_id: int, db: Session = Depends(get_db)):
item = db.query(Item).filter(Item.id == item_id).first()
return item
这种模式确保了数据库连接的正确释放,即使在发生异常的情况下。
9.2 类依赖
依赖也可以是类:
python复制class Pagination:
def __init__(self, skip: int = 0, limit: int = 100):
self.skip = skip
self.limit = limit
@app.get("/items/")
async def list_items(pagination: Pagination = Depends()):
return {"skip": pagination.skip, "limit": pagination.limit}
9.3 依赖链
依赖可以嵌套使用:
python复制def get_query_params(q: Optional[str] = None):
return q
def get_search_query(full_text: bool = False, q: str = Depends(get_query_params)):
if full_text:
return f"%{q}%"
return q
@app.get("/search/")
async def search(query: str = Depends(get_search_query)):
return {"query": query}
9.4 全局依赖
可以为整个应用或路由组设置依赖:
python复制app = FastAPI(dependencies=[Depends(verify_api_key)])
router = APIRouter(dependencies=[Depends(check_permission)])
9.5 yield 依赖(资源管理)
对于需要清理的资源,使用 yield 模式:
python复制async def get_redis():
redis = Redis()
try:
yield redis
finally:
await redis.close()
@app.get("/cache/{key}")
async def get_cache(key: str, redis: Redis = Depends(get_redis)):
value = await redis.get(key)
return {"value": value}
这种模式确保了资源(如数据库连接、文件句柄等)的正确释放。
10. 中间件
10.1 自定义中间件
创建自定义中间件:
python复制@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)
return response
中间件可以修改请求和响应,或处理异常。我在项目中常用中间件来实现日志记录、请求ID注入等功能。
10.2 CORS 中间件
处理跨域请求:
python复制from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["https://example.com"],
allow_methods=["*"],
allow_headers=["*"],
)
在生产环境中,应该严格限制 allow_origins,而不是使用通配符。
10.3 GZip 中间件
启用响应压缩:
python复制from fastapi.middleware.gzip import GZipMiddleware
app.add_middleware(GZipMiddleware, minimum_size=1000)
这对于返回大量数据的 API 可以显著减少传输时间。
10.4 信任代理中间件
当应用运行在反向代理后时:
python复制from fastapi.middleware.trustedhost import TrustedHostMiddleware
app.add_middleware(
TrustedHostMiddleware,
allowed_hosts=["example.com", "*.example.com"]
)
这个中间件会验证 Host 头,防止主机头攻击。
11. 异常处理
11.1 HTTPException
抛出 HTTP 异常:
python复制from fastapi import HTTPException
@app.get("/items/{item_id}")
async def read_item(item_id: int):
if item_id not in db:
raise HTTPException(
status_code=404,
detail="Item not found",
headers={"X-Error": "Item missing"}
)
return db[item_id]
11.2 自定义异常
注册自定义异常处理器:
python复制from fastapi import Request
from fastapi.responses import JSONResponse
class UnicornException(Exception):
def __init__(self, name: str):
self.name = name
@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}
11.3 全局异常处理
覆盖默认异常处理器:
python复制from fastapi.exceptions import RequestValidationError
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
return JSONResponse(
status_code=422,
content={"detail": exc.errors(), "body": exc.body},
)
这个功能在需要自定义验证错误响应格式时特别有用。
12. 数据库集成
12.1 SQLAlchemy 配置
配置 SQLAlchemy ORM:
python复制from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db"
engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
对于生产环境,我推荐使用 PostgreSQL 和连接池:
python复制DATABASE_URL = "postgresql://user:password@localhost/dbname"
engine = create_engine(DATABASE_URL, pool_size=5, max_overflow=10)
12.2 定义模型
创建 SQLAlchemy 模型:
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)
12.3 Pydantic Schemas
定义对应的 Pydantic 模型:
python复制class UserBase(BaseModel):
email: str
class UserCreate(UserBase):
password: str
class User(UserBase):
id: int
class Config:
orm_mode = True
orm_mode = True 允许 Pydantic 从 ORM 对象读取数据,而不仅仅是字典。
12.4 CRUD 操作
实现基本的 CRUD 操作:
python复制def get_user(db: Session, user_id: int):
return db.query(User).filter(User.id == user_id).first()
def create_user(db: Session, user: UserCreate):
hashed_password = get_password_hash(user.password)
db_user = User(email=user.email, hashed_password=hashed_password)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
12.5 路由实现
将数据库操作集成到路由:
python复制@app.post("/users/", response_model=User)
def create_user(user: UserCreate, db: Session = Depends(get_db)):
db_user = get_user_by_email(db, email=user.email)
if db_user:
raise HTTPException(400, "Email already registered")
return create_user(db=db, user=user)
这种分层架构(路由 → 服务 → 数据库)使代码更易于维护和测试。
13. 身份认证与授权
13.1 密码哈希
安全地存储密码:
python复制from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def get_password_hash(password: str):
return pwd_context.hash(password)
def verify_password(plain_password: str, hashed_password: str):
return pwd_context.verify(plain_password, hashed_password)
13.2 JWT Token
实现 JWT 认证:
python复制from datetime import datetime, timedelta
from jose import JWTError, 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})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
13.3 OAuth2 认证流程
实现密码流认证:
python复制from fastapi.security import OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
async def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
401,
detail="Could not validate credentials",
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 JWTError:
raise credentials_exception
user = get_user(db, username=username)
if user is None:
raise credentials_exception
return user
13.4 基于角色的权限控制
扩展认证系统支持角色:
python复制async def get_current_active_user(current_user: User = Depends(get_current_user)):
if current_user.disabled:
raise HTTPException(400, "Inactive user")
return current_user
async def require_admin(user: User = Depends(get_current_active_user)):
if user.role != "admin":
raise HTTPException(403, "Admin access required")
return user
@app.get("/admin/")
async def admin_panel(admin: User = Depends(require_admin)):
return {"message": "Welcome admin"}
14. 后台任务
14.1 BackgroundTasks
对于不需要立即完成的操作:
python复制from fastapi import BackgroundTasks
def write_notification(email: str, message=""):
with open("log.txt", mode="w") as email_file:
content = f"notification for {email}: {message}"
email_file.write(content)
@app.post("/send-notification/{email}")
async def send_notification(
email: str,
background_tasks: BackgroundTasks
):
background_tasks.add_task(write_notification, email, message="some notification")
return {"message": "Notification sent in the background"}
14.2 使用 Celery 处理复杂任务
对于更复杂的异步任务,集成 Celery:
python复制from celery import Celery
celery = Celery(
"tasks",
broker="redis://localhost:6379/0",
backend="redis://localhost:6379/1"
)
@celery.task
def process_large_file(file_path: str):
# 长时间处理...
return result
@app.post("/process-file/")
async def process_file(file: UploadFile = File(...)):
file_path = save_upload_file(file)
task = process_large_file.delay(file_path)
return {"task_id": task.id}
15. WebSocket
15.1 基本 WebSocket
实现 WebSocket 端点:
python复制@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
while True:
data = await websocket.receive_text()
await websocket.send_text(f"Message text was: {data}")
15.2 WebSocket 连接管理器
管理多个连接:
python复制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 broadcast(self, message: str):
for connection in self.active_connections:
await connection.send_text(message)
manager = ConnectionManager()
@app.websocket("/ws/{client_id}")
async def websocket_endpoint(websocket: WebSocket, client_id: int):
await manager.connect(websocket)
try:
while True:
data = await websocket.receive_text()
await manager.broadcast(f"Client #{client_id} says: {data}")
except WebSocketDisconnect:
manager.disconnect(websocket)
await manager.broadcast(f"Client #{client_id} left the chat")
15.3 WebSocket 客户端示例
简单的 JavaScript 客户端:
javascript复制const socket = new WebSocket("ws://localhost:8000/ws/123");
socket.onmessage = (event) => {
console.log("Message from server:", event.data);
};
socket.send("Hello Server!");
16. 测试
16.1 基本测试
使用 TestClient 测试 API:
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}
16.2 异步测试
测试异步端点:
python复制import pytest
from httpx import AsyncClient
@pytest.mark.asyncio
async def test_read_item():
async with AsyncClient(app=app, base_url="http://test") as ac:
response = await ac.get("/items/42")
assert response.status_code == 200
16.3 测试数据库
使用临时数据库进行测试:
python复制from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
TEST_DATABASE_URL = "sqlite:///./test.db"
test_engine = create_engine(TEST_DATABASE_URL)
TestingSessionLocal = sessionmaker(bind=test_engine)
@pytest.fixture
def test_db():
Base.metadata.create_all(bind=test_engine)
db = TestingSessionLocal()
try:
yield db
finally:
db.close()
Base.metadata.drop_all(bind=test_engine)
17. 部署
17.1 使用 Uvicorn 部署
生产环境启动命令:
bash复制uvicorn main:app --host 0.0.0.0 --port 80 --workers 4
推荐使用 Gunicorn 作为进程管理器:
bash复制gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app
17.2 Docker 部署
基本 Dockerfile:
dockerfile复制FROM python:3.9
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", "80"]
17.3 Nginx 配置
作为反向代理的 Nginx 配置:
nginx复制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;
}
}
17.4 Systemd 服务
创建 systemd 服务:
ini复制[Unit]
Description=FastAPI Application
After=network.target
[Service]
User=www-data
WorkingDirectory=/path/to/app
ExecStart=/usr/local/bin/gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app
Restart=always
[Install]
WantedBy=multi-user.target
18. 项目最佳实践
18.1 推荐项目结构
模块化的项目结构:
code复制.
├── app/
│ ├── __init__.py
│ ├── main.py
│ ├── dependencies.py
│ ├── routers/
│ │ ├── __init__.py
│ │ ├── items.py
│ │ └── users.py
│ ├── models/
│ │ ├── __init__.py
│ │ ├── models.py
│ │ └── schemas.py
│ ├── crud/
│ │ ├── __init__.py
│ │ ├── items.py
│ │ └── users.py
│ ├── databases/
│ │ └── database.py
│ └── config.py
├── tests/
│ ├── __init__.py
│ ├── test_main.py
│ └── test_routers/
│ ├── __init__.py
│ ├── test_items.py
│ └── test_users.py
├── requirements.txt
└── Dockerfile
18.2 配置管理
使用 Pydantic 管理配置:
python复制from pydantic import BaseSettings
class Settings(BaseSettings):
app_name: str = "Awesome API"
admin_email: str
database_url: str = "sqlite:///./sql_app.db"
class Config:
env_file = ".env"
settings = Settings()
18.3 日志配置
结构化日志配置:
python复制import logging
from logging.config import dictConfig
log_config = {
"version": 1,
"formatters": {
"json": {
"()": "pythonjsonlogger.jsonlogger.JsonFormatter",
"fmt": "%(asctime)s %(levelname)s %(message)s"
}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "json"
}
},
"root": {
"handlers": ["console"],
"level": "INFO"
}
}
dictConfig(log_config)
logger = logging.getLogger(__name__)
18.4 API 版本控制
通过路由前缀实现版本控制:
python复制app = FastAPI()
app.include_router(
items.router,
prefix="/api/v1",
tags=["items"]
)
对于重大变更,可以同时维护多个版本:
python复制app.include_router(v1.router, prefix="/api/v1")
app.include_router(v2.router, prefix="/api/v2")