Python FastAPI教程
FastAPI 是一个现代、快速(高性能)的 Python Web 框架,用于构建 API。它基于标准 Python 类型提示,使用 Starlette 和 Pydantic 构建,提供了极高的性能,接近 NodeJS 和 Go 的速度。
目录
- 安装与快速开始
- 路径操作与路由
- 请求与响应模型
- 数据验证与类型提示
- 依赖注入系统
- 数据库集成
- 用户认证与授权
- 中间件与CORS
- WebSockets
- 测试与部署
- 高级特性
安装与快速开始
安装 FastAPI 和 Uvicorn
pip install fastapi uvicorn
最小应用
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello World"}
运行开发服务器
uvicorn main:app --reload
访问 http://127.0.0.1:8000
即可看到响应,访问 http://127.0.0.1:8000/docs
可查看自动生成的交互式 API 文档。
路径操作与路由
基本路径操作
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items():
return [{"item_id": "Foo"}, {"item_id": "Bar"}]
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
HTTP 方法
@app.post("/items/")
async def create_item(item: dict):
return item
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: dict):
return {"item_id": item_id, **item}
@app.delete("/items/{item_id}")
async def delete_item(item_id: int):
return {"message": "Item deleted"}
路径参数与查询参数
from typing import Optional
@app.get("/items/{item_id}")
async def read_item(
item_id: int,
q: Optional[str] = None,
short: bool = False
):
item = {"item_id": item_id}
if q:
item.update({"q": q})
if not short:
item.update(
{"description": "This is an amazing item"}
)
return item
访问示例:/items/5?q=somequery&short=True
请求与响应模型
Pydantic 模型
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
响应模型
@app.post("/items/", response_model=Item)
async def create_item(item: Item):
return item
响应状态码
from fastapi import status
@app.post("/items/", status_code=status.HTTP_201_CREATED)
async def create_item(item: Item):
return item
自定义响应
from fastapi.responses import JSONResponse, HTMLResponse
@app.get("/custom/")
async def custom_response():
return JSONResponse(
content={"message": "Custom response"},
status_code=200,
headers={"X-Custom-Header": "value"}
)
@app.get("/html/", response_class=HTMLResponse)
async def read_html():
return """
<html>
<head><title>HTML</title></head>
<body><h1>HTML Response</h1></body>
</html>
"""
数据验证与类型提示
路径参数验证
from fastapi import Path
@app.get("/items/{item_id}")
async def read_item(
item_id: int = Path(..., title="The ID of the item", ge=1),
q: Optional[str] = Query(None, max_length=50)
):
return {"item_id": item_id, "q": q}
查询参数验证
from fastapi import Query
@app.get("/items/")
async def read_items(
q: Optional[str] = Query(
None,
min_length=3,
max_length=50,
regex="^fixedquery$"
)
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
请求体验证
from pydantic import Field
class Item(BaseModel):
name: str = Field(..., min_length=3, max_length=50)
description: Optional[str] = Field(
None, title="Description", max_length=300
)
price: float = Field(..., gt=0, description="Price must be positive")
tax: Optional[float] = None
依赖注入系统
基本依赖
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
类作为依赖
class QueryParams:
def __init__(
self,
q: Optional[str] = None,
skip: int = 0,
limit: int = 100
):
self.q = q
self.skip = skip
self.limit = limit
@app.get("/items/")
async def read_items(params: QueryParams = Depends()):
return params
子依赖
def query_extractor(q: Optional[str] = None):
return q
def query_or_cookie_extractor(
q: str = Depends(query_extractor),
last_query: Optional[str] = Cookie(None)
):
if not q:
return last_query
return q
@app.get("/items/")
async def read_query(query_or_default: str = Depends(query_or_cookie_extractor)):
return {"q_or_cookie": query_or_default}
数据库集成
SQLAlchemy 集成
from sqlalchemy import create_engine, Column, Integer, String
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()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
name = Column(String)
email = Column(String, unique=True, index=True)
# 依赖项
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(name=user.name, email=user.email)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
Tortoise-ORM (异步)
from tortoise.contrib.fastapi import register_tortoise
register_tortoise(
app,
db_url="sqlite://db.sqlite3",
modules={"models": ["models"]},
generate_schemas=True,
add_exception_handlers=True,
)
# models.py
from tortoise.models import Model
from tortoise import fields
class User(Model):
id = fields.IntField(pk=True)
name = fields.CharField(50)
email = fields.CharField(100, unique=True)
用户认证与授权
OAuth2 密码流
from fastapi.security import OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
return {"token": token}
JWT 认证
from datetime import datetime, timedelta
from jose import JWTError, jwt
from passlib.context import CryptContext
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
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: Optional[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: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
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(username)
if user is None:
raise credentials_exception
return user
@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=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
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"}
@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_user)):
return current_user
中间件与CORS
添加中间件
from fastapi import Request
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)
return response
CORS 设置
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
WebSockets
基本 WebSocket
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"Message text was: {data}")
多个客户端管理
from fastapi import WebSocket, WebSocketDisconnect
class ConnectionManager:
def __init__(self):
self.active_connections = []
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:
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.send_personal_message(f"You wrote: {data}", websocket)
await manager.broadcast(f"Client #{client_id} says: {data}")
except WebSocketDisconnect:
manager.disconnect(websocket)
await manager.broadcast(f"Client #{client_id} left the chat")
测试与部署
测试 FastAPI 应用
from fastapi.testclient import TestClient
client = TestClient(app)
def test_read_main():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Hello World"}
def test_create_item():
response = client.post(
"/items/",
json={"name": "Foo", "price": 42.0},
)
assert response.status_code == 201
assert response.json()["name"] == "Foo"
部署到生产环境
使用 Uvicorn 和 Gunicorn:
pip install gunicorn
gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app
使用 Docker:
FROM python:3.9
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
高级特性
后台任务
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"}
静态文件
from fastapi.staticfiles import StaticFiles
app.mount("/static", StaticFiles(directory="static"), name="static")
事件处理
@app.on_event("startup")
async def startup_event():
print("Application startup")
@app.on_event("shutdown")
async def shutdown_event():
print("Application shutdown")
自定义文档界面
from fastapi.openapi.utils import get_openapi
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title="Custom API",
version="2.5.0",
description="This is a very custom OpenAPI schema",
routes=app.routes,
)
app.openapi_schema = openapi_schema
return app.openapi_schema
app.openapi = custom_openapi
总结
FastAPI 的主要特点:
- 高性能:接近 NodeJS 和 Go 的速度
- 快速开发:减少约 40% 的人为错误
- 自动文档:交互式 API 文档 (Swagger UI 和 ReDoc)
- 基于标准:完全兼容 API 开放标准 OpenAPI 和 JSON Schema
- 类型安全:基于 Python 类型提示
- 异步支持:原生支持 async/await
- 依赖注入:灵活强大的依赖注入系统
FastAPI 适合构建各种规模的 API,从小型项目到大型企业级应用都能胜任。