第 3.6 章:错误处理与数据验证
学习时间: 50 分钟
1. 错误处理:“飞船的应急护盾”
即使是最完美的飞船,也可能发生意想不到的情况:
- 来自任务控制中心的错误指令: 客户端发送了不正确的数据。
- 与模块失去联系: 数据库中未找到资源。
- 反应堆故障: 服务器内部错误。
正确的错误处理是一套“应急护盾”系统。它不会让飞船解体,而是向任务控制中心发送清晰的信号,告知哪里出了问题。
💡 太空类比:
一个优秀的机载电脑不会只向任务控制中心发送“紧急情况!”的信号,而是会发送一份结构化的报告:
这使得地球上的工程师能够快速理解问题并采取措施。
2. Pydantic 数据验证:内置的“机载电脑”
我们已经见识过 Pydantic 的魔力。如果你尝试创建一个包含错误数据类型(例如,将 launch_year
设为字符串)的飞船,FastAPI 会自动返回 422 Unprocessable Entity
错误,并附带详细说明哪个字段以及为何未通过验证。
POST /spaceships
请求示例:
{
"name": "X-Wing",
"type": "Истребитель",
"launch_year": "давно", // <-- 类型错误!
"status": "В строю"
}
FastAPI 自动响应:
{
"detail": [
{
"loc": [
"body",
"launch_year"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
3. 处理“资源未找到”:HTTPException
异常
我们已经在 CRUD 操作中使用了它。HTTPException
是 FastAPI 中止请求执行并立即向客户端返回错误响应的标准方式。
回顾 GET /spaceships/{ship_id}
中的代码:
# main.py
from fastapi import FastAPI, HTTPException # 确保已导入 HTTPException
# ...
@app.get("/spaceships/{ship_id}", response_model=Spaceship, tags=["航天器"])
def get_spaceship(ship_id: int):
ship = db_spaceships.get(ship_id)
if not ship:
# 如果飞船未找到,则“抛出” 404 异常
raise HTTPException(status_code=404, detail=f"ID 为 {ship_id} 的航天器未找到")
return ship
raise HTTPException(...)
:此调用会停止函数执行。status_code=404
:设置 HTTP 响应状态码。detail
:将通过 JSON 响应体发送给客户端的消息。
4. 自定义验证器:“启动前的特殊检查”
如果我们想添加自己的、更复杂的业务逻辑怎么办?例如,禁止发射名为“死星”的飞船。
为此,Pydantic 提供了一个强大的工具——验证器。
步骤 1:在 SpaceshipCreate
模型中添加验证器
# main.py
from pydantic import BaseModel, Field, validator
class SpaceshipCreate(BaseModel):
name: str = Field(..., min_length=3, max_length=50)
type: str
launch_year: int = Field(..., gt=1950)
status: str
@validator('name')
def name_must_not_be_forbidden(cls, v):
"""检查飞船名称是否在禁止列表中。"""
if 'Death Star' in v:
raise ValueError('根据帝国法令,禁止使用“死星”之类的名称!')
return v.title() # 同时将名称转换为首字母大写
@validator('name')
:一个将此函数“绑定”到name
字段的装饰器。cls, v
:该方法接收类本身(cls
)和字段的值(v
)。raise ValueError(...)
:如果验证未通过,我们将引发标准的 Python 异常。FastAPI 会捕获它并将其转换为一个漂亮的422
错误。return v.title()
:如果一切正常,我们必须返回一个值。我们甚至可以在运行时修改它(例如,转换为标准格式)。
步骤 2:测试
重启 uvicorn
并尝试通过 /docs
创建一个带有禁用名称的飞船。你将收到一个带有你的自定义消息的 422
错误!
5. 全局错误处理:“站点的应急协议”
有时需要捕获意外错误(例如,连接到真实数据库失败)并返回统一、标准化的响应格式。
为此,可以使用 @app.exception_handler
装饰器。
示例:捕获所有 ValueError
错误
# main.py
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
# ...
@app.exception_handler(ValueError)
async def value_error_exception_handler(request: Request, exc: ValueError):
"""
所有 ValueError 错误的全局处理程序,
用于返回标准化的 JSON。
"""
return JSONResponse(
status_code=400,
content={"message": f"数据错误:{str(exc)}"},
)
@app.exception_handler(ValueError)
:告诉 FastAPI,此函数应处理所有之前未被捕获的ValueError
。async def ...
:异常处理程序必须是异步的(async
)。JSONResponse
:允许完全控制响应体和状态。
现在,当我们的自定义验证器触发时,响应将采用我们定义的更友好的格式。
巩固测验
🚀 本章总结:
你已为你的 API 飞船安装了强大的防护系统和应急协议。现在它能够:
- 🛡️ 借助 Pydantic 自动抵御“不正确数据”的攻击。
- 🚨 通过
HTTPException
妥善报告资源缺失(404 Not Found
)。 - ⚙️ 使用自定义验证器进行“特殊检查”。
- 🧯 全局捕获意外故障并给出标准化响应。
你的“超光速引擎”不仅速度快,而且极其可靠!
📌 检查:
- 尝试创建一个名为“死星”的飞船,并确认你收到了带有你的自定义消息的
400
错误。- 尝试请求
GET /spaceships/999
,并确认你收到了404
错误。- 尝试发送一个
POST
请求,将launch_year
设为字符串,并确认你收到了422
错误。⚠️ 如果有错误:
- 确保所有必需的模块(
HTTPException
、validator
、Request
、JSONResponse
)都已导入。- 检查
@validator
和@app.exception_handler
装饰器是否没有拼写错误。
恭喜你完成第 3 章! 你已经从零开始构建并启动了一个强大、有文档记录且受保护的 FastAPI API。你已准备好执行真正的太空任务。