CRUD как HTTP-контракт

Отсутствующий ресурс и HTTPException

Содержание курса

POST уже сохраняет переговорную, а GET читает её по идентификатору. Для отсутствующего id выражение rooms_db[room_id] из предыдущего урока вызывает KeyError, и клиент получает 500. Отсутствие записи нужно отличать от внутренней ошибки сервера: здесь API возвращает 404.

raise HTTPException: ответ 404 при отсутствии переговорной

Когда клиент запрашивает GET /api/rooms/99, а в rooms_db такого ключа нет, rooms_db.get(99) возвращает None. Дальше зависит от того, что делает обработчик.

Если просто написать return room и room окажется None, FastAPI без response_model пошлёт ответ 200 OK с телом null. С response_model=RoomOut ситуация хуже: Pydantic попытается валидировать None как объект с обязательными полями id, name, capacity — и упадёт с ошибкой сериализации, которую клиент получит как 500 Internal Server Error. Ни то, ни другое не является корректным ответом на «объект не найден».

Правильный HTTP-контракт для такой ситуации — статус 404 Not Found с понятным телом. В FastAPI для этого используют HTTPException из пакета fastapi:

from fastapi import FastAPI, HTTPException

В нужном месте обработчика выполняют:

raise HTTPException(status_code=404, detail="Room not found")

raise немедленно прерывает выполнение функции — никакой код после него не запустится. Стандартный обработчик исключений FastAPI перехватывает HTTPException и формирует HTTP-ответ самостоятельно: устанавливает статус из status_code и кладёт {"detail": "Room not found"} в тело с заголовком Content-Type: application/json.

Клиент, который правильно читает HTTP, сразу видит 404 и понимает: ресурс отсутствует, а не произошла серверная ошибка и не вернулся пустой результат. Это и есть ключевое отличие raise HTTPException от простого return None.