Представьте себе веб-приложение без обработки ошибок. Пользователь вводит заведомо некорректный запрос, а вместо дружелюбного сообщения об ошибке видит спад стека вызовов Python. Звучит пугающе, правда? Неправильная или, что ещё хуже, забытая обработка ошибок может сделать ваше приложение не только неудобным для пользователей, но и небезопасным.
Ошибка в коде — это как неожиданный гость: иногда её можно проигнорировать, иногда — нужно срочно чинить последствия её визита, а иногда — лучше даже заранее подготовиться, чтобы она не оказалась совсем неожиданной. В случае веб-разработки грамотная обработка ошибок помогает:
- Улучшить пользовательский опыт. Мы не хотим, чтобы пользователи видели некрасивое сообщение "500 Internal Server Error", когда можно было показать информативное сообщение вида "Запрашиваемый ресурс не найден".
- Обеспечить безопасность. Вывод необработанных ошибок может содержать детали реализации приложения, что может быть использовано злоумышленником.
- Упростить отладку. Логирование ошибок и разумный подход к обработке помогают быстро находить и исправлять баги.
Примеры ошибок
Ошибки бывают разные:
- Предсказуемые ошибки. Например, пользователь отправляет форму без указания обязательного поля, и наша логика проверки данных их корректно обрабатывает.
- Непредсказуемые ошибки. Например, база данных "упала", или сторонний API перестал отвечать — эти проблемы мы не всегда можем предвидеть, но всё равно можем подготовиться, чтобы минимизировать урон.
Если ваше приложение никак не обрабатывает ошибки, то оно работает как некий капитан Титаника — пытается плыть дальше, несмотря на айсберг впереди.
Основные принципы обработки ошибок
Давайте выделим несколько проверенных принципов, которые применяются при обработке ошибок.
Информирование пользователя
Первое и главное правило: пользователь должен быть в курсе, что что-то пошло не так. Если запрос выполняется медленно и вдруг выбрасывает ошибку, пользователю важно понимать причину. Например, при попытке заказать товар пользователь увидит сообщение вроде "К сожалению, этот товар больше недоступен", а не загадочное "Ошибка 500".
В FastAPI мы можем использовать встроенные механизмы возврата HTTP-статусов и сообщений. Например, вернём 404 код, если запрашиваемый ресурс не найден.
from fastapi import FastAPI, HTTPException
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: int):
if item_id > 10: # Предположим, что у нас только 10 предметов
raise HTTPException(status_code=404, detail="Item not found")
return {"item_id": item_id}
Когда пользователь запрашивает ресурс, которого нет, он увидит JSON-ответ:
{
"detail": "Item not found"
}
Логирование ошибок
Логирование — это наш верный друг-разработчик. Оно не только помогает быстро находить баги, но и фиксирует "взломы", неожиданные сценарии работы приложения и даже может служить доказательством при разборе инцидентов. FastAPI позволяет легко интегрировать модули логирования, такие как logging.
Пример логирования ошибки:
import logging
logger = logging.getLogger("my_logger")
@app.get("/log-error")
async def log_error_example():
try:
x = 1 / 0 # Это явно приведёт к ошибке
except ZeroDivisionError as e:
logger.error(f"Ошибка: {e}")
raise HTTPException(status_code=500, detail="Internal Server Error")
В логах вы увидите, как ошибка была зарегистрирована:
ERROR:my_logger: Ошибка: division by zero
Паниковать нельзя, логировать!
Когда приложение даёт сбой, очень важно не сделать ещё хуже. Самая частая ошибка — это показывать пользователю слишком много технической информации: отладочные сообщения, стеки вызовов и детали внутренней кухни. Это не только пугает обычного пользователя, но ещё и может выдать лишнюю информацию злоумышленнику.
Вот как делать не надо:
@app.get("/error")
async def bad_error():
return 1 / 0 # Ой...
В таком виде ошибка просто вылетит наружу без всякой попытки её поймать или объяснить. Лучше уж просто сказать, что что-то пошло не так — и не выдавать всю подноготную.
Практические сценарии обработки ошибок
Иногда простого кода raise HTTPException недостаточно — нужно подумать и о том, как ошибка будет выглядеть для пользователя. Ниже — парочка жизненных ситуаций.
Постраничное уведомление об ошибке
Иногда пользователь ожидает не JSON-ответ, а полностью кастомизированную страницу ошибки. Например, для ошибки 404 мы можем вернуть HTML-страницу.
from fastapi.responses import HTMLResponse
@app.get("/custom-404", response_class=HTMLResponse)
async def custom_not_found():
raise HTTPException(
status_code=404,
detail="<h1>Упс! Страница не найдена</h1><p>Похоже, вы заглянули не туда.</p>",
)
Посетитель увидит оптимистичное сообщение, а не грубый текст ошибки.
Сообщение с деталями для разработчиков
Зачастую разработчики тестируют API с помощью инструментов вроде Swagger или Postman. В таких случаях полезно, если система может выдавать расширенные данные для отладки, но только при соблюдении условий (например, в режиме разработки):
DEBUG_MODE = True
@app.get("/debug-error")
async def debug_error():
try:
1 / 0
except ZeroDivisionError as e:
if DEBUG_MODE:
raise HTTPException(status_code=500, detail=f"Ошибка: {e}")
else:
raise HTTPException(status_code=500, detail="Internal Server Error")
В режиме DEBUG_MODE=True мы видим точную информацию, но в продакшене такие детали останутся скрытыми.
Заключение
Теперь мы понимаем, как важно уделять внимание обработке ошибок. В следующих лекциях мы разберём более сложные темы: как обрабатывать стандартные HTTP-ошибки (например, 404 и 500), как создавать пользовательские обработчики, кастомные исключения, а также как логировать ошибки и диагностировать их с помощью middleware. Запаситесь терпением — впереди нас ждёт глубокое погружение!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ