Глава 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. Кастомные валидаторы: "Особые проверки" перед запуском
Что, если мы хотим добавить свою, более сложную бизнес-логику? Например, запретить запускать корабли с названием "Death Star".
Для этого в 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('Названия вроде "Death Star" запрещены Имперским указом!')
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.
- 🚨 Грамотно сообщать об отсутствии ресурсов (
404 Not Found
) черезHTTPException
. - ⚙️ Проводить "особые проверки" с помощью кастомных валидаторов.
- 🧯 Глобально перехватывать непредвиденные сбои и давать стандартизированные ответы.
Ваш "гипердвигатель" не только быстр, но и невероятно надежен!
📌 Проверка:
- Попробуйте создать корабль с названием "Death Star" и убедитесь, что получаете ошибку
400
с вашим кастомным сообщением.- Попробуйте запросить
GET /spaceships/999
и убедитесь, что получаете ошибку404
.- Попробуйте отправить
POST
-запрос сlaunch_year
в виде строки и убедитесь, что получаете ошибку422
.⚠️ Если ошибки:
- Убедитесь, что все необходимые модули (
HTTPException
,validator
,Request
,JSONResponse
) импортированы.- Проверьте, что декораторы
@validator
и@app.exception_handler
написаны без опечаток.
Поздравляем с завершением Главы 3! Вы с нуля построили и запустили мощное, документированное и защищенное API на FastAPI. Вы готовы к выполнению настоящих космических миссий