Отсутствующий ресурс и HTTPException
Содержание курса
get_room_or_404: общий поиск для GET /api/rooms/{room_id}
Логика «найди запись или верни 404» нужна каждый раз, когда маршрут работает с конкретным id. Чтобы не дублировать одни и те же три строки в каждом обработчике, её выносят в обычную функцию Python:
def get_room_or_404(room_id: int) -> dict:
room = rooms_db.get(room_id)
if room is None:
raise HTTPException(status_code=404, detail="Room not found")
return room
Здесь нет ничего специфичного для FastAPI: это просто вспомогательная функция. Она либо бросает исключение, либо возвращает словарь — третьего варианта нет. Обработчик GET сводится к одной строке:
@app.get("/api/rooms/{room_id}", response_model=RoomOut)
def read_room(room_id: int):
return get_room_or_404(room_id)
Полный файл main.py с общим поиском и POST для создания записи:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
app = FastAPI()
rooms_db: dict[int, dict] = {}
next_id = 1
class RoomIn(BaseModel):
name: str = Field(..., min_length=2, max_length=50)
capacity: int = Field(..., ge=1, le=50)
class RoomOut(BaseModel):
id: int
name: str
capacity: int
def get_room_or_404(room_id: int) -> dict:
room = rooms_db.get(room_id)
if room is None:
raise HTTPException(status_code=404, detail="Room not found")
return room
@app.post("/api/rooms", response_model=RoomOut, status_code=201)
def create_room(room: RoomIn):
global next_id
room_id = next_id
rooms_db[room_id] = {"id": room_id, "name": room.name, "capacity": room.capacity}
next_id += 1
return rooms_db[room_id]
@app.get("/api/rooms/{room_id}", response_model=RoomOut)
def read_room(room_id: int):
return get_room_or_404(room_id)
В одном запущенном процессе с пустым rooms_db создаём переговорную:
POST /api/rooms {"name": "Mars", "capacity": 8}
→ 201 {"id": 1, "name": "Mars", "capacity": 8}
Затем читаем существующую и несуществующую:
GET /api/rooms/1
→ 200 {"id": 1, "name": "Mars", "capacity": 8}
GET /api/rooms/99
→ 404 {"detail": "Room not found"}
Важный момент: когда get_room_or_404 бросает исключение при room_id=99, строка return get_room_or_404(room_id) в read_room никогда не получает управление обратно. FastAPI перехватывает HTTPException раньше, чем обработчик успеет что-то вернуть. Поэтому response_model=RoomOut при 404 не задействуется — сериализация запускается только при успешном возврате.
Если позже понадобится маршрут PUT /api/rooms/{room_id} или любой другой, работающий с конкретной записью, он вызовет ту же get_room_or_404 и получит тот же 404 с тем же detail — без дополнительного кода.

