3、FastAPI核心基础:路径操作、HTTP方法、请求与响应模型、状态码与异常处理

FastAPI 之所以能在短时间内成为 Python 后端开发的主流选择,核心在于它将现代类型系统与 Web 协议规范进行了深度整合。本章将围绕构建 API 的四个基础支柱展开:路径操作(如何定义 URL 端点)、HTTP 方法(如何表达语义动作)、请求与响应模型(如何用 Pydantic 保证数据契约)以及状态码与异常处理(如何让错误信息标准化)。理解这四者的协同关系,是写出健壮、可维护、高性能 API 的前提。

FastAPI 请求-响应核心链路 HTTP 客户端 浏览器 / Postman / SDK 请求 (JSON) 路径操作 @app.get("/items/{id}") 方法 + 路径 + 参数 依赖注入 参数验证 Query / Path / Body 类型 + 约束条件 Pydantic 模型 请求体 / 响应体 自动序列化与校验 业务逻辑 数据库操作 外部服务调用 计算与处理 异常处理 HTTPException 自定义异常处理器 响应 (JSON) 响应模型 response_model 状态码 + 过滤字段 状态码 200 / 201 / 404 / 422 FastAPI 通过类型注解自动完成参数解析、数据校验、序列化与文档生成

3.1 路径操作与HTTP方法:定义API的骨架

核心原理

路径操作(Path Operation)是 FastAPI 处理请求的基本单元。它由三部分组成:路径(Path)、HTTP 方法(Method)和操作函数(Operation Function)。FastAPI 通过装饰器将 URL 路径与 Python 函数绑定,当客户端发起匹配的请求时,框架自动调用对应函数并返回结果。HTTP 方法定义了请求的语义:GET 用于获取资源,POST 用于创建资源,PUT 用于整体更新,PATCH 用于部分更新,DELETE 用于删除资源。

FastAPI 的路径参数使用花括号语法声明,例如 /items/{item_id}。框架会自动从 URL 中提取该参数,并根据类型注解进行转换。路径参数默认是字符串类型,但可以声明为 int、float 等,FastAPI 会自动完成类型转换和校验。

操作流程与判断方法

  1. 定义路径:使用装饰器 @app.get()、@app.post() 等指定 HTTP 方法和 URL 路径。
  2. 声明参数:在函数签名中声明路径参数、查询参数或请求体参数。
  3. 设置响应:通过 response_model 参数定义返回数据的结构。
  4. 指定状态码:使用 status_code 参数设置默认成功状态码。
from fastapi import FastAPI, Path, Query

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(
    item_id: int = Path(..., ge=1, description="物品ID"),
    q: str | None = Query(None, max_length=50)
):
    return {"item_id": item_id, "q": q}

案例拆解:商品查询接口

假设我们要构建一个电商 API,需要支持按 ID 查询商品详情。初始需求是:客户端传入商品 ID,返回商品名称、价格和库存。约束条件包括:ID 必须为正整数,价格保留两位小数,库存不能为负数。

参数类型约束说明
item_idintge=1商品唯一标识
verbosebool默认 False是否返回详细描述

实现时,我们使用 Path 和 Query 组件声明约束条件。FastAPI 会在请求到达时自动校验参数,如果校验失败,返回 422 错误并附带详细的错误信息。这避免了在业务代码中手动编写大量 if-else 判断。

常见误区与复盘

误区1:将路径参数和查询参数混淆。路径参数是 URL 中不可省略的部分,查询参数是可选的键值对。
误区2:忽略 HTTP 方法的语义。例如,使用 GET 方法执行删除操作,虽然技术上可行,但违反了 RESTful 规范,容易造成安全隐患。
误区3:路径参数类型声明不完整。如果声明为 int 但客户端传入非数字,FastAPI 会返回 422 错误,而不是 500 错误。

3.2 请求与响应模型:用Pydantic定义数据契约

核心原理

Pydantic 是 FastAPI 的数据验证核心。通过定义继承自 BaseModel 的类,我们可以声明请求体的结构、字段类型和约束条件。FastAPI 在接收请求时,会自动将 JSON 数据解析为 Pydantic 模型实例,并执行类型校验。如果数据不合法,返回 422 错误并列出所有字段的错误信息。

响应模型(response_model)则用于控制返回数据的结构。它有两个关键作用:过滤敏感字段(如密码、内部 ID)和保证接口契约稳定。即使内部数据结构发生变化,只要响应模型不变,客户端就不会受影响。

操作流程

  1. 定义请求模型:创建 Pydantic 类,声明字段类型和默认值。
  2. 定义响应模型:创建另一个 Pydantic 类,只包含需要返回的字段。
  3. 在路径操作中使用:将请求模型作为函数参数,将响应模型传给 response_model。
from pydantic import BaseModel, Field
from typing import Optional

class ItemCreate(BaseModel):
    name: str = Field(..., min_length=1, max_length=50)
    price: float = Field(..., gt=0, le=10000)
    description: Optional[str] = None

class ItemResponse(BaseModel):
    id: int
    name: str
    price: float

@app.post("/items/", response_model=ItemResponse, status_code=201)
async def create_item(item: ItemCreate):
    # 模拟数据库操作
    return {"id": 1, **item.dict()}

案例拆解:用户注册接口

考虑一个用户注册场景。客户端提交用户名、邮箱和密码。约束条件:用户名 3-20 个字符,邮箱格式合法,密码至少 8 位且包含数字和字母。响应中不能返回密码字段。

字段请求模型响应模型
usernamestr, 3-20字符str
emailEmailStrstr
passwordstr, 8-32位不返回
created_at不接收datetime

通过分离请求和响应模型,我们确保了密码永远不会出现在响应中。即使开发者在业务逻辑中不小心返回了密码字段,FastAPI 也会根据响应模型自动过滤掉。

常见误区与复盘

经验:响应模型不仅用于过滤字段,还能自动生成 OpenAPI 文档。客户端开发者可以直接查看文档了解接口返回结构,无需阅读源码。
注意:不要将数据库模型直接作为响应模型。数据库模型通常包含内部字段(如外键、时间戳),直接暴露会增加安全风险。

3.3 状态码与异常处理:让错误信息标准化

核心原理

HTTP 状态码是客户端理解请求结果的第一层信息。FastAPI 允许在路径操作中通过 status_code 参数设置默认状态码,也可以在运行时动态返回状态码。异常处理则通过 HTTPException 类实现,开发者可以指定状态码和错误详情。FastAPI 还支持自定义异常处理器,用于处理业务逻辑中的特定异常类型。

操作流程与判断方法

  1. 设置默认状态码:在装饰器中指定 status_code=201 等。
  2. 抛出 HTTPException:在业务逻辑中检测到错误条件时,抛出包含状态码和详细信息的异常。
  3. 自定义异常处理器:使用 @app.exception_handler() 注册自定义异常类型。
from fastapi import HTTPException, Request
from fastapi.responses import JSONResponse

class ItemNotFoundError(Exception):
    pass

@app.exception_handler(ItemNotFoundError)
async def item_not_found_handler(request: Request, exc: ItemNotFoundError):
    return JSONResponse(
        status_code=404,
        content={"message": "物品不存在", "detail": str(exc)}
    )

@app.get("/items/{item_id}")
async def get_item(item_id: int):
    if item_id > 100:
        raise ItemNotFoundError(f"ID {item_id} 超出范围")
    return {"id": item_id, "name": "示例物品"}

案例拆解:订单状态流转

假设订单系统需要处理三种异常:订单不存在(404)、订单已取消(400)、库存不足(409)。初始现象是:客户端请求取消订单时,如果订单状态为"已发货",系统返回 500 错误。分析发现,业务代码没有区分不同错误场景,统一抛出了通用异常。

场景状态码错误信息
订单 ID 不存在404订单不存在
订单已取消400订单已取消,无法重复操作
库存不足409库存不足,剩余 3 件

修复方案:定义三个自定义异常类,分别对应不同场景。在业务逻辑中根据订单状态抛出对应异常。通过自定义异常处理器统一返回 JSON 格式的错误信息。修复后,客户端可以根据状态码和错误信息做出精确处理,例如提示用户刷新库存或联系客服。

常见误区与复盘

误区1:所有错误都返回 500。这会让客户端无法区分"服务器内部错误"和"客户端请求错误",增加排障难度。
误区2:错误信息过于详细。在生产环境中,避免返回堆栈跟踪或 SQL 语句,防止信息泄露。
误区3:忽略 422 校验错误。FastAPI 自动生成的 422 错误已经包含字段级错误信息,前端可以直接展示,无需额外处理。

3.4 综合实战:构建一个带完整错误处理的图书管理API

场景与约束

我们需要为图书馆构建一个图书管理 API,支持图书的增删改查。约束条件:图书 ID 为自动生成的整数,ISBN 必须唯一,价格在 0-1000 元之间。初始目标:所有接口返回标准化的 JSON 格式,错误信息包含错误码和可读消息。

实现步骤

  1. 定义数据模型:创建 BookCreate(请求模型)和 BookResponse(响应模型)。
  2. 实现 CRUD 操作:使用内存字典模拟数据库。
  3. 添加异常处理:定义 BookNotFoundError 和 DuplicateISBNError。
  4. 注册异常处理器:为每个自定义异常返回对应的状态码和错误信息。
from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel, Field
from typing import Dict, Optional

app = FastAPI()

class BookCreate(BaseModel):
    title: str = Field(..., min_length=1, max_length=100)
    isbn: str = Field(..., pattern="^[0-9]{13}$")
    price: float = Field(..., gt=0, le=1000)

class BookResponse(BookCreate):
    id: int

books: Dict[int, BookResponse] = {}
next_id = 1

@app.post("/books/", response_model=BookResponse, status_code=status.HTTP_201_CREATED)
async def create_book(book: BookCreate):
    global next_id
    # 检查 ISBN 唯一性
    for existing in books.values():
        if existing.isbn == book.isbn:
            raise HTTPException(status_code=409, detail="ISBN 已存在")
    new_book = BookResponse(id=next_id, **book.dict())
    books[next_id] = new_book
    next_id += 1
    return new_book

@app.get("/books/{book_id}", response_model=BookResponse)
async def get_book(book_id: int):
    if book_id not in books:
        raise HTTPException(status_code=404, detail="图书不存在")
    return books[book_id]

结果与复盘

通过上述实现,我们获得了三个关键收益:类型安全——所有输入输出都经过 Pydantic 校验;错误标准化——客户端可以通过状态码和 detail 字段快速定位问题;文档自动生成——FastAPI 自动生成 Swagger UI,包含所有接口的请求/响应示例。代价是需要额外编写模型类和异常处理代码,但相比手动解析 JSON 和编写 if-else 校验,维护成本显著降低。

总结:路径操作定义了 API 的"地址簿",HTTP 方法定义了"动作",Pydantic 模型定义了"数据契约",状态码与异常处理定义了"错误语言"。四者结合,构成了一个完整、健壮的 API 服务基础。在实际项目中,建议始终使用响应模型过滤敏感字段,并为每个业务异常定义明确的状态码和错误信息。