FastAPI 完整教程(从零开始)
一、FastAPI 是什么?
FastAPI 是一个基于 Python 的现代、高性能 Web 框架,专门用于构建 API(应用程序编程接口)。
用途:
- 构建 RESTful API 后端服务
- 构建 AI/ML 模型推理接口
- 构建微服务
- 为前端应用(React/Vue 等)提供数据接口
它的核心特点:
| 特点 | 说明 |
|---|---|
| 极快的速度 | 性能媲美 Node.js 和 Go,基于 Starlette 和 Pydantic 构建 |
| 自动文档 | 自动生成 Swagger UI 和 ReDoc 交互式 API 文档,无需手写 |
| 类型安全 | 基于 Python 类型提示(Type Hints),自动数据验证 |
| 异步支持 | 原生支持 async/await,轻松处理高并发请求 |
| 简洁的代码 | 相比 Flask/Django,减少约 40-60% 的样板代码 |
在哪里运行?
- 本地开发机器(macOS、Windows、Linux)
- 云服务器(配合 uvicorn/gunicorn 作为生产服务器)
- Docker 容器中
- 各类云平台(AWS Lambda、Google Cloud Run、Azure 等)
二、安装与环境搭建
创建虚拟环境(推荐)
# 创建项目目录
mkdir my-fastapi-project
cd my-fastapi-project
# 创建 Python 虚拟环境
python3 -m venv venv
# 激活虚拟环境
# macOS/Linux:
source venv/bin/activate
# Windows:
venv\Scripts\activate
# 提示符变成 (venv) 表示激活成功
安装 FastAPI
# 安装 FastAPI 和 uvicorn(ASGI 服务器,用于运行 FastAPI)
pip install fastapi uvicorn
# 安装完整版(包含所有可选依赖,如邮件验证等)
pip install "fastapi[all]"
# 验证安装
python -c "import fastapi; print(fastapi.__version__)"
第一个 FastAPI 应用
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"message": "Hello, FastAPI!"}
# 启动开发服务器
uvicorn main:app --reload
# main:模块名(main.py)
# app:FastAPI 实例名
# --reload:代码变更时自动重启(仅用于开发环境)
# 访问:
# API:http://localhost:8000
# 交互式文档(Swagger):http://localhost:8000/docs
# 备用文档(ReDoc):http://localhost:8000/redoc
三、基础用法
3.1 路径操作(路由)
FastAPI 使用装饰器定义路由,支持所有 HTTP 方法:
from fastapi import FastAPI
app = FastAPI()
# GET 请求
@app.get("/items")
def get_items():
return [{"id": 1, "name": "苹果"}, {"id": 2, "name": "香蕉"}]
# POST 请求(创建资源)
@app.post("/items")
def create_item():
return {"message": "创建成功"}
# PUT 请求(全量更新资源)
@app.put("/items/{item_id}")
def update_item(item_id: int):
return {"message": f"更新了 item {item_id}"}
# PATCH 请求(部分更新资源)
@app.patch("/items/{item_id}")
def partial_update_item(item_id: int):
return {"message": f"部分更新了 item {item_id}"}
# DELETE 请求(删除资源)
@app.delete("/items/{item_id}")
def delete_item(item_id: int):
return {"message": f"删除了 item {item_id}"}
3.2 路径参数
路径参数是 URL 中的动态部分,用 {} 声明:
@app.get("/users/{user_id}")
def get_user(user_id: int):
# FastAPI 自动将 URL 中的 user_id 转换为 int 类型
# 如果传入 "abc" 而不是数字,FastAPI 自动返回 422 错误
return {"user_id": user_id, "name": "张三"}
# 访问 /users/123 → {"user_id": 123, "name": "张三"}
# 访问 /users/abc → 422 Unprocessable Entity(类型验证失败)
# 路径参数也可以限制枚举值
from enum import Enum
class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"
@app.get("/models/{model_name}")
def get_model(model_name: ModelName):
return {"model": model_name, "message": f"使用模型:{model_name.value}"}
# 访问 /models/resnet → {"model": "resnet", "message": "使用模型:resnet"}
# 访问 /models/unknown → 422 错误(值不在枚举中)
3.3 查询参数
查询参数是 URL 中 ? 后面的键值对:
@app.get("/items")
def get_items(skip: int = 0, limit: int = 10):
# skip 和 limit 是查询参数,有默认值
# 访问 /items?skip=5&limit=20
fake_items = [{"id": i} for i in range(100)]
return fake_items[skip : skip + limit]
# 可选参数(使用 Optional 或 Union)
from typing import Optional
@app.get("/users")
def get_users(name: Optional[str] = None, active: bool = True):
# 访问 /users → name=None, active=True
# 访问 /users?name=张三&active=false
result = {"active": active}
if name:
result["name"] = name
return result
3.4 请求体(Request Body)
使用 Pydantic 模型定义请求体的结构:
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional
app = FastAPI()
# 定义数据模型
class Item(BaseModel):
name: str # 必填字段
description: Optional[str] = None # 可选字段,默认 None
price: float # 必填字段
is_available: bool = True # 可选字段,默认 True
@app.post("/items")
def create_item(item: Item):
# FastAPI 自动从请求体 JSON 解析并验证数据
# item 是一个 Item 对象,可以直接访问字段
return {
"message": "创建成功",
"item_name": item.name,
"total_price": item.price * 1.1 # 加税
}
# 请求示例(POST /items,Content-Type: application/json):
# {
# "name": "苹果",
# "price": 5.99
# }
# description 和 is_available 会使用默认值
3.5 数据验证(Pydantic)
Pydantic 提供强大的数据验证能力:
from pydantic import BaseModel, Field, validator, EmailStr
from typing import List
class UserCreate(BaseModel):
username: str = Field(..., min_length=3, max_length=50, description="用户名")
email: str = Field(..., description="邮箱地址")
age: int = Field(..., ge=0, le=150, description="年龄,必须在 0-150 之间")
password: str = Field(..., min_length=8, description="密码,至少8位")
tags: List[str] = Field(default=[], description="标签列表")
# 自定义验证器
@validator("username")
def username_must_not_contain_spaces(cls, v):
if " " in v:
raise ValueError("用户名不能包含空格")
return v.lower() # 转为小写
@app.post("/users")
def create_user(user: UserCreate):
return {"message": f"用户 {user.username} 创建成功"}
# FastAPI 会在解析请求体时自动运行所有验证器
# 验证失败时返回 422 错误,并详细说明哪个字段出了什么问题
3.6 响应模型
可以指定 API 返回的数据结构,自动过滤掉不应暴露的字段(如密码):
from pydantic import BaseModel
class UserInDB(BaseModel):
id: int
username: str
email: str
hashed_password: str # 这个字段不应该返回给前端
class UserResponse(BaseModel):
id: int
username: str
email: str
# 没有 hashed_password,FastAPI 会自动过滤掉它
@app.get("/users/{user_id}", response_model=UserResponse)
def get_user(user_id: int):
# 假设从数据库查询到的用户对象包含 hashed_password
user = {"id": user_id, "username": "zhangsan", "email": "z@example.com", "hashed_password": "xxxx"}
# 返回时 FastAPI 会自动根据 UserResponse 过滤字段,hashed_password 不会出现在响应中
return user
3.7 HTTP 状态码
from fastapi import FastAPI, HTTPException, status
app = FastAPI()
@app.get("/items/{item_id}", status_code=status.HTTP_200_OK)
def get_item(item_id: int):
if item_id > 100:
# 抛出 HTTP 异常,FastAPI 自动返回正确的错误响应
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"商品 {item_id} 不存在"
)
return {"id": item_id, "name": "商品"}
@app.post("/items", status_code=status.HTTP_201_CREATED)
def create_item(item: dict):
# 201 Created:成功创建资源时的标准状态码
return {"message": "创建成功"}
# 常用状态码:
# 200 OK - 成功
# 201 Created - 创建成功
# 204 No Content - 成功但无返回内容(如删除操作)
# 400 Bad Request - 请求参数有误
# 401 Unauthorized - 未认证
# 403 Forbidden - 无权限
# 404 Not Found - 资源不存在
# 422 Unprocessable Entity - 数据验证失败(FastAPI 自动处理)
# 500 Internal Server Error - 服务器内部错误
3.8 请求头和 Cookie
from fastapi import Header, Cookie
@app.get("/items")
def get_items(
user_agent: str = Header(None), # 获取请求头中的 User-Agent
x_token: str = Header(None), # 获取自定义请求头 X-Token
session_id: str = Cookie(None) # 获取 Cookie 中的 session_id
):
return {
"user_agent": user_agent,
"token": x_token,
"session": session_id
}
四、进阶用法
4.1 依赖注入(Dependency Injection)
依赖注入是 FastAPI 最强大的特性之一,用于复用逻辑(如认证、数据库连接、参数验证):
from fastapi import FastAPI, Depends, HTTPException, status
app = FastAPI()
# ─── 简单依赖:公共查询参数 ───────────────────────────────────────
def common_query_params(skip: int = 0, limit: int = 10):
"""所有列表接口共用的分页参数"""
return {"skip": skip, "limit": limit}
@app.get("/items")
def get_items(params: dict = Depends(common_query_params)):
# params 由 FastAPI 自动注入
return {"skip": params["skip"], "limit": params["limit"]}
@app.get("/users")
def get_users(params: dict = Depends(common_query_params)):
# 同样使用 common_query_params,无需重复定义
return {"skip": params["skip"], "limit": params["limit"]}
# ─── 认证依赖 ─────────────────────────────────────────────────────
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
security = HTTPBearer()
def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)):
"""从请求头中提取并验证 Bearer Token"""
token = credentials.credentials
if token != "my-secret-token":
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Token 无效",
headers={"WWW-Authenticate": "Bearer"},
)
return {"username": "authenticated_user"}
@app.get("/protected")
def protected_route(current_user: dict = Depends(get_current_user)):
# 只有 Token 验证通过才能访问这个接口
return {"message": f"欢迎,{current_user['username']}"}
4.2 异步编程(async/await)
FastAPI 原生支持异步操作,适合 I/O 密集型任务(如数据库查询、调用外部 API):
import asyncio
import httpx
from fastapi import FastAPI
app = FastAPI()
# 同步路由(普通函数)
@app.get("/sync")
def sync_route():
# 同步操作,适合 CPU 密集型或调用同步库
import time
time.sleep(1) # 会阻塞整个服务器!生产环境避免这样做
return {"type": "sync"}
# 异步路由(协程函数)
@app.get("/async")
async def async_route():
# 异步操作,等待期间服务器可以处理其他请求
await asyncio.sleep(1) # 非阻塞的等待
return {"type": "async"}
# 异步调用外部 API
@app.get("/weather/{city}")
async def get_weather(city: str):
async with httpx.AsyncClient() as client:
response = await client.get(f"https://api.weather.example.com/{city}")
return response.json()
# 并发调用多个 API
@app.get("/dashboard")
async def get_dashboard():
async with httpx.AsyncClient() as client:
# 同时发起多个请求,总时间等于最慢那个,而不是所有请求时间之和
user_resp, orders_resp, stats_resp = await asyncio.gather(
client.get("http://user-service/api/users/me"),
client.get("http://order-service/api/orders/recent"),
client.get("http://stats-service/api/stats/today"),
)
return {
"user": user_resp.json(),
"orders": orders_resp.json(),
"stats": stats_resp.json(),
}
4.3 数据库集成(SQLAlchemy + 异步)
# 安装依赖:pip install sqlalchemy asyncpg databases
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import declarative_base, sessionmaker
from sqlalchemy import Column, Integer, String
from fastapi import FastAPI, Depends
DATABASE_URL = "postgresql+asyncpg://user:password@localhost/dbname"
# 创建异步数据库引擎
engine = create_async_engine(DATABASE_URL, echo=True)
# 创建 Session 工厂
AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
Base = declarative_base()
# 定义数据库模型
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String, unique=True, index=True)
email = Column(String, unique=True)
app = FastAPI()
# 数据库 Session 依赖
async def get_db():
async with AsyncSessionLocal() as session:
try:
yield session # 注入到路由函数中
await session.commit()
except Exception:
await session.rollback()
raise
# 使用数据库的路由
from sqlalchemy.future import select
@app.get("/users/{user_id}")
async def get_user(user_id: int, db: AsyncSession = Depends(get_db)):
result = await db.execute(select(User).where(User.id == user_id))
user = result.scalar_one_or_none()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
return {"id": user.id, "username": user.username}
@app.post("/users")
async def create_user(username: str, email: str, db: AsyncSession = Depends(get_db)):
user = User(username=username, email=email)
db.add(user)
await db.flush() # 获取自动生成的 id
return {"id": user.id, "username": user.username}
4.4 中间件(Middleware)
中间件在请求到达路由之前和响应返回给客户端之前执行,适合做日志、跨域、限流等:
import time
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# ─── CORS 中间件(跨域资源共享)──────────────────────────────────
# 允许前端(如 React)跨域请求这个 API
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000", "https://my-frontend.com"], # 允许的来源
allow_credentials=True, # 允许携带 Cookie
allow_methods=["*"], # 允许所有 HTTP 方法
allow_headers=["*"], # 允许所有请求头
)
# ─── 自定义中间件:请求耗时日志 ──────────────────────────────────
@app.middleware("http")
async def log_request_time(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)
print(f"{request.method} {request.url.path} - {process_time:.3f}s")
return response
4.5 背景任务(Background Tasks)
用于在返回响应后异步执行耗时操作(如发送邮件、写日志):
from fastapi import FastAPI, BackgroundTasks
app = FastAPI()
def send_welcome_email(email: str, username: str):
"""这是一个耗时操作,在后台执行"""
import time
time.sleep(5) # 模拟发送邮件的耗时
print(f"欢迎邮件已发送到 {email},用户:{username}")
@app.post("/users/register")
def register_user(
email: str,
username: str,
background_tasks: BackgroundTasks # FastAPI 自动注入
):
# 立即返回响应给用户,不需要等待邮件发送完成
background_tasks.add_task(send_welcome_email, email, username)
return {"message": "注册成功,欢迎邮件将稍后发送"}
4.6 WebSocket 支持
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from typing import List
app = FastAPI()
# 管理连接的类
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_id} 说:{data}")
except WebSocketDisconnect:
manager.disconnect(websocket)
await manager.broadcast(f"客户端 {client_id} 已离线")
4.7 JWT 认证完整示例
# 安装:pip install python-jose[cryptography] passlib[bcrypt]
from datetime import datetime, timedelta
from typing import Optional
from jose import JWTError, jwt
from passlib.context import CryptContext
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel
SECRET_KEY = "your-super-secret-key-change-this-in-production"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
app = FastAPI()
# 模拟用户数据库
fake_users_db = {
"zhangsan": {
"username": "zhangsan",
"hashed_password": pwd_context.hash("secret123"),
}
}
class Token(BaseModel):
access_token: str
token_type: str
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
to_encode = data.copy()
expire = datetime.utcnow() + (expires_delta or timedelta(minutes=15))
to_encode.update({"exp": expire})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
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, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user = fake_users_db.get(username)
if user is None:
raise credentials_exception
return user
@app.post("/token", response_model=Token)
def login(form_data: OAuth2PasswordRequestForm = Depends()):
user = fake_users_db.get(form_data.username)
if not user or not pwd_context.verify(form_data.password, user["hashed_password"]):
raise HTTPException(status_code=400, detail="用户名或密码错误")
access_token = create_access_token(
data={"sub": user["username"]},
expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
)
return {"access_token": access_token, "token_type": "bearer"}
@app.get("/users/me")
def read_users_me(current_user: dict = Depends(get_current_user)):
return {"username": current_user["username"]}
4.8 完整项目结构(生产环境推荐)
my-fastapi-project/
├── app/
│ ├── __init__.py
│ ├── main.py # FastAPI 实例,注册路由
│ ├── config.py # 配置管理(读取环境变量)
│ ├── database.py # 数据库连接、Session 管理
│ ├── models/ # SQLAlchemy 数据库模型
│ │ ├── __init__.py
│ │ ├── user.py
│ │ └── item.py
│ ├── schemas/ # Pydantic 请求/响应模型
│ │ ├── __init__.py
│ │ ├── user.py
│ │ └── item.py
│ ├── routers/ # 路由模块(按功能拆分)
│ │ ├── __init__.py
│ │ ├── users.py
│ │ └── items.py
│ ├── services/ # 业务逻辑层
│ │ ├── __init__.py
│ │ └── user_service.py
│ └── dependencies.py # 公共依赖(认证、数据库 Session)
├── tests/
│ ├── test_users.py
│ └── test_items.py
├── Dockerfile
├── docker-compose.yml
├── requirements.txt
└── .env # 环境变量(不提交到 Git!)
main.py(注册路由模块):
from fastapi import FastAPI
from app.routers import users, items
from app.database import engine, Base
app = FastAPI(title="My API", version="1.0.0")
# 注册路由模块(APIRouter)
app.include_router(users.router, prefix="/api/v1/users", tags=["用户"])
app.include_router(items.router, prefix="/api/v1/items", tags=["商品"])
@app.on_event("startup")
async def startup():
# 应用启动时创建数据库表
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
routers/users.py:
from fastapi import APIRouter, Depends
from app.dependencies import get_db, get_current_user
from app.schemas.user import UserCreate, UserResponse
router = APIRouter()
@router.get("/{user_id}", response_model=UserResponse)
async def get_user(user_id: int, db=Depends(get_db)):
...
@router.post("/", response_model=UserResponse, status_code=201)
async def create_user(user: UserCreate, db=Depends(get_db)):
...
4.9 测试
# tests/test_users.py
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
def test_create_user():
response = client.post(
"/api/v1/users/",
json={"username": "testuser", "email": "test@example.com", "password": "secret123"}
)
assert response.status_code == 201
assert response.json()["username"] == "testuser"
def test_get_nonexistent_user():
response = client.get("/api/v1/users/99999")
assert response.status_code == 404
# 运行测试
# pytest tests/ -v
五、常见问题排查
| 问题 | 原因 | 解决方法 |
|---|---|---|
| 返回 422 Unprocessable Entity | 请求数据不符合 Pydantic 模型验证 | 查看响应体中的 detail 字段,检查数据格式 |
| CORS 错误 | 未配置 CORSMiddleware | 添加 CORSMiddleware 并配置正确的 allow_origins |
| 循环导入 | 模块之间相互引用 | 检查导入路径,使用延迟导入解决 |
| async 函数中调用同步阻塞操作 | 会阻塞事件循环 | 使用 run_in_executor 或改用异步库 |
| Pydantic v1 vs v2 不兼容 | 版本升级后 API 有变化 | 安装指定版本 pydantic==1.10.x 或按 v2 文档迁移 |
教程结束。建议先运行第一个 Hello World,访问 http://localhost:8000/docs 体验自动文档,然后逐步尝试数据验证和依赖注入。
2万+

被折叠的 条评论
为什么被折叠?



