Когда ваш API растёт, ошибки — неизбежная часть жизни. И нет ничего хуже, чем получать стандартное «Internal Server Error» без намёка на причину. FastAPI предлагает мощные инструменты для обработки ошибок и создания информативных, пользовательских ответов. Сегодня разберём, как сделать ваше приложение более дружелюбным к разработчикам и пользователям.

Почему важна правильная обработка ошибок?

Представьте ситуацию: вы вызываете метод API, и вместо ожидаемого ответа получаете:

{"detail": "Internal Server Error"}

Это примерно как получить пустую чашку вместо кофе в кафе. Вы не знаете, что пошло не так и почему. Хорошая обработка ошибок даёт:

  • Понятные сообщения для разработчиков (чтобы легче было отладить).
  • Дружественные ответы для клиентов API (чтобы не отпугнуть пользователей).
  • Безопасность, скрывая внутренние детали системы.

Стандартные ошибки FastAPI

FastAPI из коробки предоставляет несколько стандартных HTTP-исключений:

  • HTTPException — основа для создания пользовательских ошибок.
  • RequestValidationError — ошибки валидации данных (обработку можно кастомизировать).

Пример обработки HTTPException

Создадим маршрут, который вызывает ошибку, если товар не найден:

from fastapi import FastAPI, HTTPException

app = FastAPI()

fake_products = {"1": "Ноутбук", "2": "Смартфон"}

@app.get("/products/{product_id}")
def read_product(product_id: str):
    if product_id not in fake_products:
        raise HTTPException(status_code=404, detail="Товар не найден")
    return {"product": fake_products[product_id]}

Если перейти на http://127.0.0.1:8000/products/3, FastAPI вернёт:

{
    "detail": "Товар не найден"
}

Что происходит:

  • При отсутствии товара вызывается HTTPException.
  • FastAPI автоматически формирует ответ с нужным статусом и сообщением.

Кастомизация обработки ошибок

Допустим, мы хотим перехватить все 404 ошибки и вернуть более дружелюбный ответ. Для этого создадим обработчик исключений.

from fastapi.responses import JSONResponse
from fastapi.requests import Request

@app.exception_handler(HTTPException)
async def custom_http_exception_handler(request: Request, exc: HTTPException):
    return JSONResponse(
        status_code=exc.status_code,
        content={"message": f"Упс! Ошибка: {exc.detail}"}
    )

Теперь 404 ошибки будут возвращать:

{"message": "Упс! Ошибка: Товар не найден"}

Обработка ошибок валидации данных

Если в запросе что-то не так (например, невалидные данные), FastAPI вызывает исключение RequestValidationError. Давайте обработаем его.

from fastapi.exception_handlers import request_validation_exception_handler
from fastapi.exceptions import RequestValidationError

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    return JSONResponse(
        status_code=422,
        content={"errors": exc.errors(), "message": "Ошибка валидации данных"}
    )

Теперь при передаче неверных данных в запросе, клиент получит подробное описание ошибки.

Возвращение кастомных ответов

FastAPI позволяет не ограничиваться JSON-ответами. Вы можете возвращать HTML, файлы или даже кастомные объекты. Рассмотрим несколько примеров.

Возвращение HTML

Иногда нужно вернуть HTML-страницу вместо JSON (например, для простой ошибки 404):

from fastapi.responses import HTMLResponse

@app.get("/html", response_class=HTMLResponse)
async def read_html():
    return """
    <html>
        <body>
            <h1>Привет, FastAPI!</h1>
        </body>
    </html>
    """

FastAPI автоматически определит тип ответа как text/html.

Файловые ответы

Что если нужно отдать файл клиенту? FastAPI справится и с этим!

from fastapi.responses import FileResponse

@app.get("/download")
async def download_file():
    return FileResponse("example.pdf", media_type='application/pdf', filename="download.pdf")

Теперь по маршруту /download пользователь скачает файл example.pdf.

Ответ с кастомным статусом и заголовками

Можно управлять статусом и заголовками ответа:

from fastapi.responses import JSONResponse

@app.get("/custom-response")
async def custom_response():
    return JSONResponse(
        status_code=201,
        content={"message": "Ресурс успешно создан"},
        headers={"X-Custom-Header": "MyValue"}
    )

Заключение

Правильная обработка ошибок и кастомные ответы — основа качественного API. Сегодня мы научились ловить исключения, кастомизировать обработку ошибок и возвращать различные типы ответов. В следующей статье разберёмся с базами данных и посмотрим, как подключить их к FastAPI.