В этой лекции мы погрузимся в стандартные HTTP-ошибки, которые являются неотъемлемой частью работы веб-приложений, и рассмотрим их обработку в FastAPI. Разберём такие статус-коды, как 404 (Not Found) и 500 (Internal Server Error), их природу, а также узнаем, как с ними работать так, чтобы не превращать жизнь пользователей и разработчиков в IT-антиутопию.
HTTP-ошибки — это способ сервера уведомить клиента (браузер или другое приложение) о том, что что-то пошло не так. Сервер возвращает HTTP-статус-код, который представляет собой трёхзначное число, передающее смысл произошедшего. Вот небольшая "группа поддержки" самых популярных категорий кодов ошибок:
- 1xx: Информационные (например, 102 — Processing). Эти встречаются редко и нас не очень интересуют.
- 2xx: Всё OK (например, 200 — ОК). Настолько всё хорошо, что об этом даже говорить не нужно.
- 3xx: Перенаправления (например, 301 — Moved Permanently). Эти нас тоже не сильно волнуют.
- 4xx: Клиентские ошибки. Вроде 404 — Not Found — "Ты просишь найти несуществующего динозавра".
- 5xx: Серверные ошибки. Например, 500 — Internal Server Error — "Наш сервер упал, отправляем программиста на починку".
Сегодня мы сосредоточимся на двух наиболее часто встречающихся: 404 и 500.
404 — Not Found: "Этого тут нет, попробуй другую дверь"
Ошибка 404 сигнализирует, что клиент запросил какой-то ресурс (например, /user/42), но сервер не смог его найти. Это может быть вызвано:
- Неправильным URL (например,
/userr/42вместо/user/42). - Отсутствием соответствующего эндпоинта на сервере.
- Ресурсом, который был удалён или никогда не существовал.
Обработка ошибки 404 в FastAPI
FastAPI автоматически возвращает 404 в тех случаях, когда маршрута (эндоинта) нет в приложении. Однако, чтобы улучшить пользовательский опыт, мы можем кастомизировать обработку этой ошибки.
from fastapi import FastAPI, HTTPException
app = FastAPI()
@app.get("/user/{user_id}")
async def get_user(user_id: int):
if user_id != 1: # У нас есть только пользователь с id=1
raise HTTPException(status_code=404, detail="Пользователь не найден")
return {"user_id": user_id, "name": "Иван Иванов"}
Пользовательская страница для 404
FastAPI позволяет настроить кастомный обработчик для 404, чтобы вместо стандартного JSON-ответа возвращать что-то более "человечное":
from fastapi.responses import JSONResponse
@app.exception_handler(404)
async def custom_404_handler(request, exc):
return JSONResponse(
status_code=404,
content={"message": "Ой! То, что вы искали, не найдено. Попробуйте другой запрос."}
)
Теперь, если пользователь случайно отправит запрос на несуществующий ресурс, ему будет выдано более дружественное сообщение.
500 — Internal Server Error: когда сервер "упал"
Ошибка 500 означает, что что-то пошло не так не на стороне клиента (как в случае 404), а на стороне сервера. Чаще всего она появляется из-за:
- Необработанных исключений (например, деление на ноль или отсутствие доступа к базе данных).
- Неправильной конфигурации сервера.
- Багов в коде (а когда их у нас не бывает?).
Пример необработанной ошибки:
@app.get("/divide")
async def divide_by_zero():
return {"result": 1 / 0} # Деление на ноль
Как только пользователь попробует этот эндпоинт, сервер вернёт 500 с большим стэктрейсом в консоли.
Обработка ошибки 500 в FastAPI
Для начала, нам нужно научиться ловить такие ошибки, чтобы не оставлять пользователя в состоянии паники. Простое решение — глобальный обработчик ошибок:
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError
@app.exception_handler(Exception)
async def global_exception_handler(request, exc):
return JSONResponse(
status_code=500,
content={"message": "Упс! Что-то пошло не так на сервере. Мы уже чиним это!"}
)
Теперь, даже если мы забудем обработать конкретную ошибку, глобальный обработчик возьмёт это на себя и отправит пользователю понятное сообщение.
Сравнение обработки 404 и 500
| Аспект | Ошибка 404 | Ошибка 500 |
|---|---|---|
| Причина | Ресурс не найден | Внутренний сбой сервера |
| Ответ по умолчанию | 404 JSON (Not Found) | 500 JSON (Internal Server Error) |
| Кто виноват? | Обычно пользователь (клиент) | Обычно разработчик (сервер) |
| Как фиксить | Настройка маршрутов и проверка данных | Логирование, мониторинг и отладка кода |
Полный пример кастомизации обработки ошибок
Давайте создадим API, которое обрабатывает и 404, и 500, предоставляя кастомные сообщения для обеих ошибок:
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse
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, "name": f"Item {item_id}"}
# Кастомный обработчик 404
@app.exception_handler(404)
async def not_found_handler(request: Request, exc: Exception):
return JSONResponse(
status_code=404,
content={"message": "Ресурс не найден. Проверьте URL."}
)
# Глобальный обработчик 500
@app.exception_handler(Exception)
async def server_error_handler(request: Request, exc: Exception):
return JSONResponse(
status_code=500,
content={"message": "На сервере произошла ошибка. Мы уже знаем об этом!"}
)
Практическая польза и типичные ошибки
- Выявление причин. Использование HTTP-кодов (4xx, 5xx) помогает быстро диагностировать проблемные области в приложении.
- Избегайте утечек информации. Никогда не показывайте пользователю стэктрейс или внутренние ошибки приложений, особенно в 500.
- Логирование спасёт мир. Все ошибки должны быть зафиксированы с помощью инструментов логирования, чтобы вы могли анализировать их позже.
Теперь ваше API подготовлено к правильной обработке ошибок 404 и 500, и пользователи не испугаются неожиданных ответов сервера! Мы с вами настроили как дружественные сообщения для клиентов, так и средства для облегчения отладки для разработчиков.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ