FastAPI 入门指南与最佳实践 – wiki基地

FastAPI 入门指南与最佳实践

FastAPI 是一个现代、快速(高性能)的 Python Web 框架,用于使用标准 Python 类型提示(Type Hints)构建 API。它结合了 Starlette(用于 Web 部分)和 Pydantic(用于数据部分),旨在提供卓越的开发速度、高性能以及自动生成交互式 API 文档。

为什么选择 FastAPI?

FastAPI 因其出色的特性而迅速成为 Python Web 开发者的首选框架之一:

  • 极高的性能: 与 NodeJS 和 Go 等高性能框架相媲美,得益于 Starlette 的异步特性和 Pydantic 的高效数据验证。
  • 开发效率高: 显著提高开发速度,并通过类型提示减少约 40% 的人为错误,提供强大的编辑器支持(包括自动补全)。
  • 易于使用: 设计直观,学习曲线平缓,让开发者能够快速上手。
  • 数据验证: 基于 Pydantic,自动提供强大的数据验证和序列化功能。
  • 标准化: 完全兼容 OpenAPI (以前称为 Swagger) 和 JSON Schema 等开放标准。
  • 自动文档: 自动生成交互式 API 文档(Swagger UI 和 ReDoc),无需额外配置。

1. 安装

在开始之前,请确保您的系统上安装了 Python 3.8 或更高版本。强烈建议使用虚拟环境来隔离项目依赖。

首先,创建并激活一个虚拟环境(可选但强烈推荐):

“`bash

创建虚拟环境

python -m venv venv

激活虚拟环境 (macOS/Linux)

source venv/bin/activate

激活虚拟环境 (Windows)

.\venv\Scripts\activate

“`

然后,安装 FastAPI 和 ASGI 服务器(如 Uvicorn)。Uvicorn 是运行 FastAPI 应用程序的推荐服务器:

“`bash
pip install “fastapi[standard]”

“[standard]” 会自动安装 uvicorn 和一些常用的依赖,如 Pydantic。

如果想单独安装,也可以这样:

pip install fastapi uvicorn

“`

2. 基础应用 (Hello World)

创建一个名为 main.py 的文件,并添加以下代码:

“`python
from fastapi import FastAPI

创建 FastAPI 应用程序实例

app = FastAPI()

定义一个 GET 请求的路由

@app.get(“/”)
async def read_root():
return {“message”: “Hello, World!”}
“`

在终端中运行应用程序:

bash
uvicorn main:app --reload

  • main 指的是 main.py 文件。
  • app 指的是 main.py 文件中创建的 FastAPI() 实例。
  • --reload 选项让服务器在检测到代码更改时自动重新加载,非常适用于开发阶段。

现在,您可以在浏览器中访问 http://127.0.0.1:8000,将看到 {"message": "Hello, World!"}
FastAPI 还会自动为您生成交互式 API 文档,访问 http://127.0.0.1:8000/docs (Swagger UI) 或 http://127.0.0.1:8000/redoc (ReDoc) 即可查看。

3. 路径参数 (Path Parameters)

路径参数是 URL 路径中用于识别特定资源的动态部分。您可以在路由中使用花括号 {} 来定义它们,FastAPI 会自动将这些值提取并作为函数参数传递。

“`python
from fastapi import FastAPI

app = FastAPI()

@app.get(“/items/{item_id}”)
async def read_item(item_id: int): # 使用类型提示自动进行数据类型转换和验证
return {“item_id”: item_id}
“`

在上述例子中,item_id: int 不仅定义了参数名,还指定了其类型为整数。如果尝试访问 /items/abc,FastAPI 将自动返回一个 422 Unprocessable Entity 错误,因为 abc 无法转换为整数。

重要提示:当存在固定路径(如 /users/me)和动态路径(如 /users/{user_id})时,路由的定义顺序非常重要。固定路径应该在动态路径之前定义,以避免动态路径错误地捕获固定路径。

4. 查询参数 (Query Parameters)

查询参数是附加在 URL 后面、以问号 ? 开头、并用 & 分隔的键值对。它们通常用于过滤、排序或分页数据。

在 FastAPI 中,任何未在路径中声明的函数参数都会被自动识别为查询参数:

“`python
from fastapi import FastAPI
from typing import Optional

app = FastAPI()

@app.get(“/items/”)
async def read_items(skip: int = 0, limit: int = 10, q: Optional[str] = None):
results = {“items”: [{“item_id”: “Foo”}, {“item_id”: “Bar”}]}
if q:
results.update({“q”: q})
return results
“`

  • skip: int = 0limit: int = 10 是带有默认值的查询参数,这意味着它们是可选的。
  • q: Optional[str] = None 表示 q 是一个可选的字符串查询参数,如果未提供,则默认为 None

FastAPI 还提供了 Query 函数,允许您为查询参数添加更丰富的验证规则和元数据(例如,设置最小/最大长度、正则表达式等)。

5. 请求体 (Request Body) 与 Pydantic

对于 POSTPUTPATCH 等 HTTP 方法,客户端通常会通过请求体发送结构化数据(通常是 JSON 格式)。FastAPI 通过与 Pydantic 的深度集成,使得定义、验证和序列化请求体变得异常简单和高效。

首先,定义一个 Pydantic 模型来描述您期望的请求体数据结构:

“`python
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional

app = FastAPI()

定义一个 Pydantic 模型来描述 Item

class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
tax: Optional[float] = None

@app.post(“/items/”)
async def create_item(item: Item): # FastAPI 会自动验证传入的数据
item_dict = item.dict()
if item.tax:
price_with_tax = item.price + item.tax
item_dict.update({“price_with_tax”: price_with_tax})
return item_dict
“`

当您在路由函数中将 Pydantic 模型作为参数的类型提示时,FastAPI 会自动完成以下操作:
* 读取传入的请求体。
* 将 JSON 数据解析为 Python 字典。
* 使用您定义的 Pydantic 模型验证数据。
* 如果验证失败,自动返回一个 422 Unprocessable Entity 错误响应。
* 将验证后的数据作为 Pydantic 模型实例传递给您的函数。

6. 响应模型 (Response Model)

FastAPI 的响应模型允许您清晰地定义 API 将返回的数据结构。这不仅有助于确保数据一致性,还能让 FastAPI 自动生成准确的 OpenAPI 文档。

使用 @app.post (或其他 HTTP 方法装饰器) 的 response_model 参数来指定响应的 Pydantic 模型:

“`python
from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional

app = FastAPI()

class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
tax: Optional[float] = None

定义一个用于响应的 Pydantic 模型,可能只包含部分字段

class ItemOut(BaseModel):
name: str
price: float

@app.post(“/items/”, response_model=ItemOut) # 指定响应模型
async def create_item(item: Item):
# 假设这里有一些处理逻辑,最终返回一个 Item 实例
return item
“`

  • response_model 确保 API 返回的数据严格符合 ItemOut 模型的结构和类型。
  • 如果函数实际返回的数据包含 ItemOut 中未定义的额外字段,FastAPI 会自动过滤掉这些多余的数据,这对于防止意外暴露敏感信息非常有用。
  • 它也是 OpenAPI 文档中响应模式的基础。

7. 依赖注入 (Dependency Injection)

依赖注入 (DI) 是一种强大的软件设计模式,它允许您向函数或类提供其所需的依赖项,而不是让它们在内部自行创建。FastAPI 内置了一个强大而直观的依赖注入系统,可以帮助您:

  • 共享逻辑: 例如,数据库会话、外部 API 客户端等。
  • 强制执行安全性: 如用户身份验证和权限检查。
  • 提高可测试性: 轻松模拟依赖项进行单元测试。
  • 改善代码模块化和可维护性

“`python
from fastapi import FastAPI, Depends, HTTPException, status
from typing import Annotated

app = FastAPI()

这是一个简单的依赖项,模拟用户验证

async def get_current_user(token: Annotated[str, Depends()]): # 从请求中获取 token
if token != “fake-super-secret-token”:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=”Invalid authentication credentials”,
headers={“WWW-Authenticate”: “Bearer”},
)
return {“username”: “currentuser”}

@app.get(“/users/me/”)
async def read_users_me(current_user: Annotated[dict, Depends(get_current_user)]):
return current_user
“`

read_users_me 函数中,current_user: Annotated[dict, Depends(get_current_user)] 声明了 read_users_me 依赖于 get_current_user 函数。FastAPI 会在调用 read_users_me 之前自动执行 get_current_user,并将其返回的结果注入到 current_user 参数中。

8. 最佳实践

8.1. 项目结构

一个良好组织的项目结构对于应用程序的可维护性、可扩展性和团队协作至关重要。以下是一个推荐的 FastAPI 项目结构示例:

my_project/
├── app/
│ ├── main.py # FastAPI 应用的入口点,注册路由和事件处理器
│ ├── api/ # 包含所有 API 端点,通常按功能或资源组织
│ │ ├── __init__.py
│ │ ├── endpoints/ # 具体的 API 路由文件 (e.g., items.py, users.py)
│ │ │ ├── __init__.py
│ │ │ ├── items.py
│ │ │ └── users.py
│ │ └── api_v1.py # API 版本聚合 (可选,用于 /api/v1/ 前缀)
│ ├── core/ # 核心配置和设置 (例如,环境变量、安全配置)
│ │ ├── __init__.py
│ │ └── config.py
│ ├── crud/ # 数据库 CRUD (Create, Read, Update, Delete) 操作
│ │ ├── __init__.py
│ │ └── item.py
│ ├── db/ # 数据库连接和会话管理 (例如,SQLAlchemy 或 Tortoise-ORM)
│ │ ├── __init__.py
│ │ └── session.py
│ ├── models/ # 数据库模型定义 (例如,SQLAlchemy 模型)
│ │ ├── __init__.py
│ │ └── item.py
│ ├── schemas/ # Pydantic 模型,用于请求体验证和响应数据序列化
│ │ ├── __init__.py
│ │ └── item.py
│ ├── services/ # 业务逻辑层,与 API 路由解耦
│ │ ├── __init__.py
│ │ └── item_service.py
│ └── dependencies.py # 共享依赖项,如认证依赖
├── tests/ # 单元测试和集成测试
│ ├── __init__.py
│ └── test_items.py
├── .env # 环境变量配置文件
├── requirements.txt # 项目依赖清单
└── README.md # 项目说明

8.2. 安全性

API 安全性是任何生产应用程序不可或缺的一部分。在 FastAPI 中实施以下最佳实践:

  • 使用 HTTPS: 在生产环境中,始终通过反向代理(如 Nginx 或 Caddy)强制使用 HTTPS,以加密所有敏感通信。
  • 输入验证和清理: FastAPI 结合 Pydantic 提供了强大的输入验证功能,可以有效防止 SQL 注入、XSS 攻击等常见漏洞。始终验证和清理所有用户输入。
  • 身份验证和授权:
    • 使用 OAuth2 (尤其是 Bearer Token 机制) 和 JWT (JSON Web Tokens) 进行用户身份验证。
    • 对用户密码进行安全哈希处理(例如,使用 passlib 库的 bcryptArgon2 算法),切勿存储明文密码。
    • 实现基于角色的访问控制 (RBAC) 或基于属性的访问控制 (ABAC) 来管理用户权限。
  • 速率限制: 实施 API 速率限制以防止滥用、爬虫和 DDoS 攻击。可以使用 fastapi-limiter 等第三方库。
  • CORS (跨域资源共享): 正确配置 CORS 策略,只允许受信任的域访问您的 API,避免不必要的安全风险。
  • 安全管理秘密: 永远不要在代码中硬编码 API 密钥、数据库凭据或其他敏感信息。使用环境变量、秘密管理服务或配置文件来安全地存储和访问这些信息。
  • 禁用生产环境中的调试模式: 在生产环境中,将 FastAPI 应用程序的调试模式设置为 False,以防止在错误响应中泄露敏感的配置或调试信息。
  • 保护 API 文档: 在生产环境中,考虑禁用或限制对交互式 API 文档 (/docs/redoc) 的访问,以防止未经授权的信息泄露。
  • 审计依赖项: 定期使用 pip-audit 等工具扫描项目依赖项,检查是否存在已知的安全漏洞。

8.3. 测试

全面的测试是确保 API 质量、可靠性和可维护性的关键。

  • 使用 pytest: pytest 是 Python 最受欢迎的测试框架,因其简洁的语法和强大的功能而广泛使用。
  • 使用 TestClient: FastAPI 提供了内置的 TestClient,它基于 httpx 库,可以模拟对 FastAPI 应用程序的 HTTP 请求,而无需实际启动和运行服务器。这使得编写单元测试和集成测试变得非常方便。

以下是一个简单的测试示例:

“`python

tests/test_main.py

from fastapi.testclient import TestClient
from app.main import app # 假设你的 FastAPI 实例在 app/main.py 中

client = TestClient(app)

def test_read_root():
response = client.get(“/”)
assert response.status_code == 200
assert response.json() == {“message”: “Hello, World!”}

def test_read_item_valid():
response = client.get(“/items/1”)
assert response.status_code == 200
assert response.json() == {“item_id”: 1}

def test_read_item_invalid_type():
response = client.get(“/items/abc”)
assert response.status_code == 422 # Pydantic 会抛出 422 错误

def test_create_item():
response = client.post(
“/items/”,
json={“name”: “Test Item”, “price”: 10.99}
)
assert response.status_code == 200
assert response.json() == {“name”: “Test Item”, “price”: 10.99}

def test_create_item_invalid_data():
response = client.post(
“/items/”,
json={“name”: “Test Item”} # 缺少 price 字段
)
assert response.status_code == 422 # Pydantic 验证失败
“`

测试最佳实践:
* 隔离测试: 确保每个测试都是独立的,不依赖于其他测试的状态或共享资源。
* 使用 Fixtures: 利用 pytest 的 fixtures 来设置和清理测试环境,例如模拟数据库连接或用户认证。
* 模拟外部依赖: 对于数据库、外部 API 或其他耗时操作,使用 mock 对象来隔离被测试的代码,提高测试速度和可靠性。
* 测试 Pydantic 模型: 确保您的 Pydantic 模型能够正确验证数据并抛出适当的错误。
* 测试覆盖率: 使用 pytest-cov 等工具检查代码覆盖率,确保您的测试覆盖了大部分业务逻辑。

8.4. 部署

将 FastAPI 应用程序部署到生产环境需要仔细规划,以确保高性能、可伸缩性和安全性。

  • 生产级 ASGI 服务器: 在生产环境中,应使用 Gunicorn (结合 Uvicorn Worker) 或 Hypercorn 等生产级 ASGI 服务器来运行 FastAPI 应用程序,它们提供了进程管理、负载均衡等功能。
  • 反向代理: 配置 Nginx 或 Caddy 等反向代理服务器,它们可以处理 SSL/TLS 终止、静态文件服务、请求路由、负载均衡和压缩等功能,从而提高性能和安全性。
  • 进程管理器: 使用 systemdsupervisorpm2 (如果使用 Node.js 生态工具) 等进程管理器来管理和监控您的应用程序进程,确保应用程序在崩溃后能自动重启。
  • 容器化: 使用 Docker 对您的应用程序进行容器化是现代部署的常见实践。Docker 容器提供了一致的运行环境,简化了部署和扩展。
  • 缓存: 对于频繁访问或计算成本高昂的数据,实施缓存策略(例如,使用 Redis 或 Memcached)可以显著减少数据库负载和响应时间。

总结

FastAPI 是一个功能强大、高性能且易于使用的 Python Web 框架,非常适合构建现代、健壮的 API。通过遵循本指南中概述的入门步骤和最佳实践,您可以构建出高效、可维护、安全且可扩展的应用程序,从而充分发挥 FastAPI 的潜力。

滚动至顶部