当前位置:首页 > 学习笔记 > 正文内容

FastAPI 后端开发实战教程:从入门到生产部署

廖万里20小时前学习笔记1

FastAPI 是现代 Python Web 开发的性能王者,基于 Starlette 和 Pydantic 构建,自动生成交互式文档,支持类型提示和异步编程。本教程将带你从零开始掌握 FastAPI 的核心特性和最佳实践。

一、FastAPI 简介与核心优势

FastAPI 是一个现代、高性能的 Python Web 框架,专为构建 API 而设计。自 2018 年发布以来,它迅速成为 Python 后端开发的热门选择,被 Netflix、Uber、Microsoft 等知名公司采用。

1.1 为什么选择 FastAPI?

FastAPI 的核心设计理念是"快速、开发者友好和类型驱动",它具有以下突出优势:

  • 极致性能:基于 Starlette(Web 部分)和 Pydantic(数据部分)构建,性能与 Node.js 和 Go 相当,是 Python 中最快的 Web 框架之一
  • 自动文档生成:通过类型注解自动生成交互式 API 文档(Swagger UI 和 ReDoc),无需手动编写文档
  • 类型驱动开发:利用 Python 类型提示自动进行数据验证、序列化和反序列化,在开发时捕获错误
  • 原生异步支持:完全支持 async/await,在处理 I/O 密集型任务时表现出色
  • 编辑器支持:完善的类型提示让 IDE 能提供出色的自动补全和错误检查

1.2 典型应用场景

FastAPI 适用于多种场景:

  • 构建微服务架构中的数据接口
  • 开发高并发的实时数据处理服务
  • 为移动应用或前端提供后端 API
  • AI 模型服务化部署
  • 数据处理管道和 ETL 系统

二、环境搭建与第一个应用

2.1 环境准备

确保你的系统安装了 Python 3.8 或更高版本。推荐使用虚拟环境隔离项目依赖:

# 创建虚拟环境
python3 -m venv fastapi-env

# 激活虚拟环境
source fastapi-env/bin/activate  # macOS/Linux
# fastapi-env\Scripts\activate  # Windows

# 安装 FastAPI 和 Uvicorn
pip install "fastapi[standard]"

fastapi[standard] 会安装 FastAPI 及其所有可选依赖,包括 Uvicorn ASGI 服务器。

2.2 编写第一个 API

创建 main.py 文件,编写一个简单的 Hello World API:

from fastapi import FastAPI

# 创建 FastAPI 应用实例
app = FastAPI(
    title="我的第一个 API",
    description="FastAPI 入门示例",
    version="1.0.0"
)

# 定义根路径的 GET 请求处理
@app.get("/")
async def read_root():
    """返回欢迎消息"""
    return {"message": "Hello, FastAPI!", "status": "success"}

# 带路径参数的路由
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str = None):
    """
    获取指定 ID 的项目
    
    - item_id: 项目 ID(整数类型,自动验证)
    - q: 可选的查询参数
    """
    result = {"item_id": item_id}
    if q:
        result.update({"q": q})
    return result

2.3 运行应用

使用 Uvicorn 运行应用:

uvicorn main:app --reload

参数说明:

  • main:Python 文件名(main.py)
  • app:FastAPI 实例名
  • --reload:热重载,代码修改后自动重启

启动后访问 http://127.0.0.1:8000 即可看到返回的 JSON 数据。

2.4 自动生成的交互式文档

FastAPI 最大的亮点之一是自动生成的 API 文档:

  • Swagger UIhttp://127.0.0.1:8000/docs - 可直接在浏览器中测试 API
  • ReDochttp://127.0.0.1:8000/redoc - 美观的文档展示界面

三、核心概念详解

3.1 路径参数

路径参数是从 URL 路径中提取的值,FastAPI 会自动进行类型转换和验证:

from fastapi import FastAPI, HTTPException

app = FastAPI()

# 模拟数据库
items_db = {
    1: {"name": "Python 编程", "price": 59.9},
    2: {"name": "FastAPI 实战", "price": 79.9},
}

@app.get("/items/{item_id}")
async def get_item(item_id: int):
    """根据 ID 获取商品信息"""
    if item_id not in items_db:
        raise HTTPException(status_code=404, detail="商品不存在")
    return {
        "item_id": item_id,
        "item": items_db[item_id]
    }

# 支持路径参数的枚举类型
from enum import Enum

class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"

@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
    """获取指定模型信息"""
    return {
        "model": model_name,
        "message": f"这是 {model_name.value} 模型"
    }

3.2 查询参数

查询参数是 URL 中 ? 后面的键值对,常用于过滤、分页等场景:

from typing import Optional, List

@app.get("/products/")
async def list_products(
    skip: int = 0,              # 分页偏移量,默认 0
    limit: int = 10,            # 每页数量,默认 10
    category: Optional[str] = None,  # 可选的分类过滤
    min_price: float = 0,       # 最低价格
    max_price: float = 10000    # 最高价格
):
    """
    商品列表接口,支持分页和过滤
    
    访问示例:/products/?skip=0&limit=5&category=books
    """
    # 模拟数据库查询
    products = [
        {"id": 1, "name": "商品A", "price": 99.9},
        {"id": 2, "name": "商品B", "price": 199.9},
        {"id": 3, "name": "商品C", "price": 29.9},
    ]
    
    return {
        "skip": skip,
        "limit": limit,
        "total": len(products),
        "products": products[skip:skip + limit]
    }

3.3 请求体与 Pydantic 模型

对于 POST、PUT 等请求,使用 Pydantic 模型定义请求体结构和验证规则:

from pydantic import BaseModel, Field, validator
from datetime import datetime
from typing import Optional

# 定义数据模型
class ProductCreate(BaseModel):
    """创建商品的数据模型"""
    name: str = Field(..., min_length=2, max_length=100, description="商品名称")
    price: float = Field(..., gt=0, description="商品价格,必须大于 0")
    description: Optional[str] = Field(None, max_length=500, description="商品描述")
    category: str = Field(..., description="商品分类")
    stock: int = Field(default=0, ge=0, description="库存数量")
    
    @validator("name")
    def name_must_not_contain_special_chars(cls, v):
        if any(char in v for char in ["<", ">", "&"]):
            raise ValueError("商品名称不能包含特殊字符")
        return v

class ProductResponse(ProductCreate):
    """商品响应模型"""
    id: int
    created_at: datetime
    updated_at: datetime

# 模拟数据库
products_db = []
product_id_counter = 0

@app.post("/products/", response_model=ProductResponse, status_code=201)
async def create_product(product: ProductCreate):
    """
    创建新商品
    
    FastAPI 会自动:
    1. 验证请求数据是否符合 ProductCreate 模型
    2. 转换数据类型
    3. 返回清晰的错误信息
    """
    global product_id_counter
    product_id_counter += 1
    
    product_dict = product.dict()
    product_dict.update({
        "id": product_id_counter,
        "created_at": datetime.now(),
        "updated_at": datetime.now()
    })
    
    products_db.append(product_dict)
    return product_dict

四、依赖注入系统

依赖注入是 FastAPI 最强大的特性之一,可以优雅地处理数据库连接、认证、权限等通用逻辑:

from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from typing import Generator

# 模拟数据库连接
class Database:
    def __init__(self):
        self.connected = True
        
    def query(self, sql: str):
        return f"查询结果: {sql}"
        
    def close(self):
        self.connected = False

# 数据库依赖
def get_db() -> Generator:
    """获取数据库连接,使用 yield 确保资源释放"""
    db = Database()
    try:
        yield db
    finally:
        db.close()  # 请求结束后自动关闭连接

# 认证依赖
security = HTTPBearer()

async def get_current_user(
    credentials: HTTPAuthorizationCredentials = Depends(security)
):
    """获取当前用户"""
    token = credentials.credentials
    
    # 模拟 token 验证
    if token != "valid-token":
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="无效的认证凭证"
        )
    
    return {"id": 1, "username": "admin"}

# 使用依赖的路由
@app.get("/users/me")
async def read_users_me(
    current_user: dict = Depends(get_current_user),
    db: Database = Depends(get_db)
):
    """
    获取当前用户信息
    
    自动注入:
    - current_user: 经过认证的用户
    - db: 数据库连接
    """
    return {
        "user": current_user,
        "db_status": "connected" if db.connected else "disconnected"
    }

五、异步编程最佳实践

FastAPI 完全支持异步编程,正确使用 async/await 可以显著提升性能:

import httpx
import asyncio
from typing import List

# 异步 HTTP 客户端
async def fetch_url(url: str) -> dict:
    """异步获取 URL 内容"""
    async with httpx.AsyncClient() as client:
        response = await client.get(url)
        return {"url": url, "status": response.status_code}

@app.get("/batch-fetch")
async def batch_fetch_urls(urls: List[str]):
    """
    并发获取多个 URL
    
    使用 asyncio.gather 实现并发请求
    """
    tasks = [fetch_url(url) for url in urls]
    results = await asyncio.gather(*tasks, return_exceptions=True)
    
    return {
        "total": len(urls),
        "results": results
    }

5.1 何时使用 async/await

  • 使用 async def:网络请求、数据库查询、文件 I/O 等 I/O 密集型操作
  • 使用普通 def:CPU 密集型计算,FastAPI 会自动放到线程池执行
  • 避免在 async 中执行阻塞操作:如 time.sleep()、同步的数据库调用

六、错误处理与异常

FastAPI 提供了完善的错误处理机制:

from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError

app = FastAPI()

# 自定义异常
class ItemNotFoundException(Exception):
    def __init__(self, item_id: int):
        self.item_id = item_id

# 全局异常处理器
@app.exception_handler(ItemNotFoundException)
async def item_not_found_handler(request: Request, exc: ItemNotFoundException):
    """自定义异常处理"""
    return JSONResponse(
        status_code=404,
        content={
            "error": "ITEM_NOT_FOUND",
            "message": f"商品 ID {exc.item_id} 不存在",
            "path": request.url.path
        }
    )

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    """请求验证错误处理"""
    return JSONResponse(
        status_code=422,
        content={
            "error": "VALIDATION_ERROR",
            "message": "请求数据验证失败",
            "details": exc.errors()
        }
    )

@app.get("/items/{item_id}")
async def get_item(item_id: int):
    """可能抛出自定义异常的接口"""
    if item_id > 100:
        raise ItemNotFoundException(item_id)
    
    if item_id < 0:
        raise HTTPException(
            status_code=400,
            detail="商品 ID 不能为负数"
        )
    
    return {"item_id": item_id, "name": "示例商品"}

七、项目结构最佳实践

对于中大型项目,推荐使用清晰的目录结构:

project/
├── app/
│   ├── __init__.py
│   ├── main.py              # 应用入口
│   ├── config.py            # 配置管理
│   ├── dependencies.py      # 公共依赖
│   ├── routers/             # 路由模块
│   │   ├── __init__.py
│   │   ├── users.py
│   │   └── products.py
│   ├── models/              # 数据模型
│   │   ├── __init__.py
│   │   ├── user.py
│   │   └── product.py
│   ├── schemas/             # Pydantic 模型
│   │   ├── __init__.py
│   │   ├── user.py
│   │   └── product.py
│   ├── services/            # 业务逻辑
│   │   ├── __init__.py
│   │   └── user_service.py
│   └── utils/               # 工具函数
│       └── __init__.py
├── tests/                   # 测试文件
│   └── test_main.py
├── alembic/                 # 数据库迁移
├── .env                     # 环境变量
├── requirements.txt
└── Dockerfile

7.1 配置管理

# app/config.py
from pydantic_settings import BaseSettings
from functools import lru_cache

class Settings(BaseSettings):
    """应用配置"""
    app_name: str = "FastAPI 应用"
    debug: bool = False
    
    # 数据库配置
    database_url: str
    
    # 安全配置
    secret_key: str
    access_token_expire_minutes: int = 30
    
    class Config:
        env_file = ".env"

@lru_cache()
def get_settings() -> Settings:
    """获取配置(缓存)"""
    return Settings()

# 在路由中使用
@app.get("/info")
async def app_info(settings: Settings = Depends(get_settings)):
    return {"app_name": settings.app_name}

八、部署与生产实践

8.1 使用 Gunicorn + Uvicorn

# 安装
pip install gunicorn uvicorn[standard]

# 启动命令(多 worker)
gunicorn main:app \
    --workers 4 \
    --worker-class uvicorn.workers.UvicornWorker \
    --bind 0.0.0.0:8000 \
    --timeout 120

8.2 Docker 部署

# Dockerfile
FROM python:3.11-slim

WORKDIR /app

# 安装依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 复制应用代码
COPY ./app ./app

# 非 root 用户运行
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser

# 启动命令
CMD ["gunicorn", "app.main:app", "-w", "4", "-k", "uvicorn.workers.UvicornWorker", "-b", "0.0.0.0:8000"]

8.3 生产环境安全配置

from fastapi import FastAPI

# 生产环境禁用文档
app = FastAPI(
    title="生产 API",
    docs_url=None,      # 禁用 Swagger UI
    redoc_url=None,     # 禁用 ReDoc
    openapi_url=None    # 禁用 OpenAPI schema
)

# 或者通过环境变量控制
import os

DEBUG = os.getenv("DEBUG", "false").lower() == "true"

app = FastAPI(
    docs_url="/docs" if DEBUG else None,
    redoc_url="/redoc" if DEBUG else None
)

总结

FastAPI 凭借其高性能、自动文档生成、类型安全等特性,已成为 Python Web 开发的首选框架。本教程涵盖了从环境搭建到生产部署的完整流程,核心要点包括:

  • 类型驱动:利用 Pydantic 模型实现数据验证和文档生成
  • 异步优先:正确使用 async/await 提升 I/O 性能
  • 依赖注入:优雅地管理共享逻辑和资源
  • 项目结构:清晰的模块划分,便于维护和扩展
  • 生产部署:多 worker、容器化、安全配置

掌握这些核心概念后,你可以快速构建高性能、可维护的 API 服务。FastAPI 的生态还在不断发展,结合 SQLAlchemy 2.0、Pydantic v2 等现代工具,Python 后端开发体验已经达到新的高度。

本文链接:https://www.kkkliao.cn/?id=987 转载需授权!

分享到:

版权声明:本文由廖万里的博客发布,如需转载请注明出处。


发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。