Skip to main content

Python 开发 - Web开发

Python Web 开发:FastAPI 实战指南

引言

FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API。它基于标准的 Python 类型提示,并提供了许多开箱即用的功能,如自动交互式文档、数据验证和依赖注入。本文将以一个实际的 AI 招聘项目为例,深入探讨 FastAPI 的核心概念和最佳实践。

项目结构

一个组织良好的项目结构是高效开发的基础。下面是我们示例项目的目录结构:

.
├── api
│   └── v1
│       ├── auth.py
│       ├── recuitment.py
│       └── ...
├── celery_server
│   ├── celery_app.py
│   └── tasks.py
├── schema
│   ├── aitask
│   ├── recuitment
│   └── ...
├── utils
│   ├── connections
│   │   ├── async_milvus.py
│   │   └── ...
│   └── tools.py
├── config.py
├── dependencies.py
├── main.py
├── requirements.txt
└── test
  • main.py: 应用入口,负责初始化 FastAPI 应用和中间件。
  • config.py: 存放应用的配置信息,如数据库连接字符串、API 密钥等。
  • dependencies.py: 定义了项目范围内的依赖注入项。
  • requirements.txt: 列出了项目的所有 Python 依赖。
  • api/v1/: 存放不同版本的 API 路由。这种组织方式便于 API 的版本管理。
  • schema/: 包含了所有的 Pydantic 模型,用于请求和响应的数据验证与序列化。
  • utils/: 包含工具函数和数据库连接模块。
  • celery_server/: 存放 Celery 相关的配置和异步任务。
  • test/: 包含集成测试和单元测试。

核心概念

应用启动与生命周期

FastAPI 提供了生命周期事件,允许我们在应用启动和关闭时执行代码。在我们的项目中,我们使用 @asynccontextmanager 来管理应用的生命周期,这是一种更现代、更 Pythonic 的方式。

// main.py

from fastapi import FastAPI
from contextlib import asynccontextmanager

@asynccontextmanager
async def lifespan(app: FastAPI):
    # 应用启动时执行
    print("Application startup")
    app.state.db_connection = await connect_to_db()
    yield
    # 应用关闭时执行
    await app.state.db_connection.close()
    print("Application shutdown")

app = FastAPI(lifespan=lifespan)

在我们的实际项目中,lifespan 函数负责初始化和关闭数据库连接池(MySQL, Redis, Milvus)以及其他服务(如 MinIO 和 OpenAI)。

依赖注入

依赖注入是 FastAPI 的核心功能之一。它允许我们将依赖项(如数据库会话、认证用户等)声明为函数参数,FastAPI 会在处理请求时自动创建和管理这些依赖。

// dependencies.py

from fastapi import Depends, HTTPException, status
from .utils.connections import get_db_session

async def get_current_user(token: str = Depends(oauth2_scheme)):
    # ... 从 token 中获取用户 ...
    return user

def get_database_session():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

在路由函数中,我们可以像这样使用依赖项:

// api/v1/recuitment.py

from fastapi import APIRouter, Depends
from ..dependencies import get_current_user

router = APIRouter()

@router.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_user)):
    return current_user

路由与 API 版本管理

将路由分散到不同的模块中,可以使代码更加清晰和易于维护。我们的项目通过 APIRouter 来实现这一点,并将所有路由集中在 api/v1/ 目录下。

// main.py

from api.v1 import all_routers

app = FastAPI()
common_prefix = "/api/v1" 

for router_instance in all_routers:
    app.include_router(router_instance, prefix=common_prefix)

这种方式不仅使得代码结构清晰,也为未来的 API 版本升级(如 /api/v2)提供了便利。

数据模型与验证

FastAPI 使用 Pydantic 模型进行数据验证、序列化和文档生成。通过在 schema/ 目录中为不同的业务逻辑定义模型,我们可以确保 API 的输入和输出都符合预期的格式。

// schema/recuitment/PositionBaseData.py

from pydantic import BaseModel
from typing import Optional

class PositionBaseData(BaseModel):
    title: str
    description: Optional[str] = None
    salary: float

在 API 操作中,我们可以直接使用这些模型作为类型提示:

// api/v1/recuitment.py

from fastapi import APIRouter
from ...schema.recuitment import PositionBaseData

router = APIRouter()

@router.post("/positions/")
async def create_position(position: PositionBaseData):
    # ... 创建职位的逻辑 ...
    return position

配置文件管理

使用独立的配置文件来管理应用的设置是一种最佳实践。在我们的项目中,config.py 使用了 pydantic-settings 库,它可以从环境变量或 .env 文件中加载配置。

// config.py

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    app_name: str = "AI Recruitment API"
    database_url: str
    openai_api_key: str

    class Config:
        env_file = ".env"

settings = Settings()

异步任务

对于耗时的操作,如发送邮件、处理文件等,我们应该使用异步任务来避免阻塞主线程。Celery 是一个流行的任务队列,可以与 FastAPI 很好地集成。

celery_server/ 目录下,我们定义了 Celery 应用实例和任务:

// celery_server/celery_app.py

from celery import Celery

app = Celery("tasks", broker="redis://localhost:6379/0", backend="redis://localhost:6379/0")

app.conf.update(
    task_serializer="json",
    accept_content=["json"],
    result_serializer="json",
    timezone="Asia/Shanghai",
    enable_utc=True,
)
// celery_server/tasks.py

from .celery_app import app

@app.task
def process_resume(resume_id: int):
    # ... 处理简历的耗时逻辑 ...
    return {"status": "success", "resume_id": resume_id}

数据库集成

我们的项目集成了多种数据库和存储服务,并通过 utils/connections 模块进行统一管理。这种模块化的设计使得数据库的切换和扩展变得更加容易。

  • MySQL: 使用 aiomysql 提供异步连接池。
  • Redis: 用于缓存和 Celery 的消息代理。
  • Milvus: 一个开源的向量数据库,用于 AI 相关的相似性搜索。
  • MinIO: 一个兼容 S3 的对象存储服务,用于存储文件(如简历)。

通过在应用的生命周期中管理这些连接,我们确保了资源的有效利用和优雅关闭。

测试

测试是保证软件质量的关键。我们的项目包含了 test/unit-test/ 两个目录,分别用于集成测试和单元测试。FastAPI 提供了 TestClient,可以方便地对 API 进行测试。

// test/test_main.py

from fastapi.testclient import TestClient
from ..main import app

client = TestClient(app)

def test_read_main():
    response = client.get("/")
    assert response.status_code == 200
    assert response.json() == {"msg": "Hello World"}

总结

FastAPI 凭借其卓越的性能、易用性和强大的功能,已成为现代 Python Web 开发的热门选择。通过本文对一个实际项目的剖析,我们深入了解了 FastAPI 在项目结构、依赖注入、数据验证、异步任务和数据库集成等方面的最佳实践。希望这篇指南能为你的 FastAPI 开发之旅提供有益的参考。