FastAPI 完整教程

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 体验自动文档,然后逐步尝试数据验证和依赖注入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

漫漫曼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值