Представьте: ваше приложение отлично справляется со своей задачей, всё работает, пользователи довольны. Но вдруг — бац! — прилетает ошибка, которую вы не предусмотрели. Может, клиент отправил неожиданные данные, а, может, вы забыли проверить какой-то тип — и всё, приложение рушится или возвращает нечто странное.
Чтобы такие сюрпризы не превращались в кошмар, нужен глобальный обработчик ошибок. Он подстрахует вас на случай, если где-то что-то пошло не так и локальная обработка не сработала. Такой обработчик может красиво вернуть понятное сообщение пользователю, записать ошибку в лог для анализа, и, самое главное, не выдать наружу чувствительные детали вроде отладочной информации или трейса.
Как работает глобальный обработчик в FastAPI?
FastAPI предоставляет удобный способ настройки глобального обработчика ошибок через специальный декоратор @app.exception_handler. Он позволяет зарегистрировать обработчик для всех или определённых типов исключений.
Приведём пример глобального обработчика.
Предположим, у нас есть API для работы с заметками:
from fastapi import FastAPI
app = FastAPI()
@app.get("/notes/{note_id}")
async def get_note(note_id: int):
# Здесь может возникнуть ошибка, если ID недопустим
if note_id < 0:
raise ValueError("ID должен быть положительным числом")
return {"note_id": note_id, "content": "Заметка найдена!"}
Если пользователь передаст отрицательный note_id, Python выбросит исключение ValueError, которое, если не обработать, покажет пользователю нечитабельный текст ошибки. Теперь мы настроим глобальный обработчик для таких ситуаций.
Настройка глобального обработчика через @app.exception_handler
Глобальные обработчики настраиваются следующим образом:
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
# Глобальный обработчик для исключений ValueError
@app.exception_handler(ValueError)
async def value_error_handler(request: Request, exc: ValueError):
# Возвращаем JSON-ответ с описанием ошибки
return JSONResponse(
status_code=400,
content={"error": "Некорректный запрос", "details": str(exc)},
)
@app.get("/notes/{note_id}")
async def get_note(note_id: int):
if note_id < 0:
raise ValueError("ID должен быть положительным числом")
return {"note_id": note_id, "content": "Заметка найдена!"}
Теперь, если пользователь передаст отрицательный note_id, он получит аккуратный JSON-ответ с описанием проблемы:
{
"error": "Некорректный запрос",
"details": "ID должен быть положительным числом"
}
Обработка всех необработанных исключений
Что, если ошибка не относится к ValueError? Для этого в FastAPI можно создать универсальный обработчик, который перехватывает все необработанные исключения.
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
# Логируем ошибку (например, с подробным трейсбэком)
print(f"Unhandled error: {exc}")
return JSONResponse(
status_code=500,
content={"error": "Произошла внутренняя ошибка сервера"}
)
Теперь любое исключение, которое не перехватили другие обработчики, будет корректно обработано этим глобальным обработчиком.
Обработка кастомных исключений
Мы можем создать собственный класс исключений и обработчик для него. Например:
class BusinessLogicError(Exception):
def __init__(self, message: str):
self.message = message
@app.exception_handler(BusinessLogicError)
async def business_logic_error_handler(request: Request, exc: BusinessLogicError):
return JSONResponse(
status_code=400,
content={"error": "Ошибка бизнес-логики", "details": exc.message},
)
@app.get("/raise-business-error")
async def raise_error():
raise BusinessLogicError("Произошло нарушение бизнес-правил")
При вызове эндпоинта /raise-business-error пользователь получит понятное сообщение:
{
"error": "Ошибка бизнес-логики",
"details": "Произошло нарушение бизнес-правил"
}
Интеграция с логированием
Глобальный обработчик часто используется для записи критических ошибок в журналы логов. Это можно сделать с помощью встроенного модуля Python logging:
import logging
logger = logging.getLogger(__name__)
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
# Логируем ошибку
logger.error(f"Unhandled error: {exc}", exc_info=True)
return JSONResponse(
status_code=500,
content={"error": "Произошла внутренняя ошибка сервера"}
)
Совет: настройте ротацию логов и подключите мониторинговые системы (например, Sentry или ELK Stack).
Перехват ошибок внешних API
Работая с внешними API, можно комбинировать глобальный обработчик с библиотекой httpx. Например, если сторонний сервис возвращает ошибку:
import httpx
@app.get("/external-api")
async def call_external_api():
async with httpx.AsyncClient() as client:
response = await client.get("https://api.example.com/resource")
if response.status_code != 200:
raise ValueError("Ошибка внешнего API")
return response.json()
Если возникнет ошибка, глобальный обработчик для ValueError обеспечит её обработку. Это делает работу с внешними сервисами более предсказуемой.
Типичные ошибки при настройке глобального обработчика
- Необработанные исключения: если вы забыли настроить обработчик для исключений типа
Exception, неприятные ошибки могут доходить до конечных пользователей. - Избыточный возврат информации: никогда не возвращайте стеки ошибок в JSON — это может содержать чувствительную информацию. Пользователям достаточно получить краткое сообщение.
- Логирование с отладочной информацией: не забывайте использовать уровни логирования (
DEBUG,INFO,ERROR), чтобы ваши журналы были читаемыми.
Теперь ваш FastAPI-приложение подготовлено к обработке ошибок на всех уровнях: и локально, и глобально. Благодаря этому ваш API будет устойчивым, предсказуемым и легко отлаживаемым.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ