Как реализовать зависимости (Dependencies) в FastAPI? Приведите пример.

Ответ

Зависимости (Dependencies) в FastAPI — это мощный механизм для инъекции общей логики в обработчики маршрутов. Они позволяют переиспользовать код, упрощать тестирование и структурировать приложение, например, для аутентификации, авторизации, валидации данных или работы с базой данных.

Почему это важно:

  • Переиспользование кода: Избегайте дублирования логики в разных эндпоинтах.
  • Тестируемость: Зависимости легко мокать при тестировании.
  • Структурирование: Четкое разделение ответственности.

Пример: Зависимость для проверки API-ключа

Рассмотрим зависимость, которая проверяет наличие и корректность API-ключа, переданного в заголовке запроса.

from fastapi import FastAPI, Depends, HTTPException, Header, status

app = FastAPI()

# 1. Определение функции-зависимости
def verify_api_key(api_key: str = Header(..., description="API-ключ для доступа")):
    """
    Проверяет API-ключ, переданный в заголовке 'api-key'.
    """
    if api_key != "super-secret-key": # Используйте более надежный способ хранения ключа в реальных проектах
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Неверный API-ключ"
        )
    return api_key

# 2. Использование зависимости в маршруте
@app.get("/protected-route/", dependencies=[Depends(verify_api_key)])
async def protected_route():
    """
    Эндпоинт, доступный только при наличии валидного API-ключа.
    """
    return {"message": "Доступ разрешен"}

# 3. Использование зависимости для получения значения
@app.get("/items/")
async def read_items(current_api_key: str = Depends(verify_api_key)):
    """
    Эндпоинт, который использует API-ключ, возвращенный зависимостью.
    """
    return {"message": f"Доступ с ключом: {current_api_key}", "items": ["item1", "item2"]}

В этом примере:

  • Функция verify_api_key определена как зависимость. Она ожидает api_key из заголовка api-key.
  • Если ключ неверный, генерируется HTTPException со статусом 403 Forbidden.
  • В первом маршруте (/protected-route/) зависимость используется только для проверки доступа (dependencies=[Depends(verify_api_key)]).
  • Во втором маршруте (/items/) зависимость также проверяет доступ, но её возвращаемое значение (current_api_key) инжектируется как параметр в функцию обработчика.

Зависимости могут быть асинхронными функциями, классами, или даже другими зависимостями, формируя цепочки.

Ответ 18+ 🔞

А, ну так, слушай, смотри, блядь. Зависимости в FastAPI — это ж, ёпта, как волшебная палочка для ленивых инженеров, сука. Вместо того чтобы в каждом эндпоинте одно и то же говно копипастить, ты эту логику в одно место засунешь и потом просто подключаешь, где надо. Красота, блядь, в рот меня чих-пых!

Ну и нахуй это вообще нужно, спросишь?

  • Код не повторяй, мудила! Один раз написал проверку прав — и тыкай её во все ручки, как горячие пирожки.
  • Тестировать — одно удовольствие. Подменил эту зависимость в тестах — и всё, блядь, система думает, что ты Бог и царь.
  • Порядок в голове и в коде. Аутентификация — отдельно, доступ к базе — отдельно, валидация — отдельно. Не превращай свой код в солянку из борща и компота, ёпта.

Смотри, как это выглядит на практике: проверка какого-нибудь ёбанного API-ключа

Представь, у тебя есть секретный ключ, и без него — ни ногой. Вот как это делается.

from fastapi import FastAPI, Depends, HTTPException, Header, status

app = FastAPI()

# 1. Вот она, наша палочка-выручалочка, функция-зависимость
def verify_api_key(api_key: str = Header(..., description="API-ключ для доступа")):
    """
    Смотрит, прислали ли нам в заголовке 'api-key' правильный пропуск.
    Если нет — сразу в пизду, с таким не разговариваем.
    """
    if api_key != "super-secret-key": # В жизни, конечно, не хардкоди, а то будешь потом охуевать.
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Неверный API-ключ, дружок-пирожок"
        )
    return api_key # Если ключ прошёл — возвращаем его, пусть пользуется.

# 2. Используем зависимость просто как стражника у ворот
@app.get("/protected-route/", dependencies=[Depends(verify_api_key)])
async def protected_route():
    """
    Сюда просто так не зайдешь. Нужен тот самый ключ.
    """
    return {"message": "Доступ разрешен, проходи, не задерживайся"}

# 3. А тут мы не только пускаем, но и ключ себе забираем, чтобы посмотреть
@app.get("/items/")
async def read_items(current_api_key: str = Depends(verify_api_key)):
    """
    Зависимость отработала, ключ проверила, и заодно отдала его нам в руки.
    Можем, например, в логи записать, какой красавчик зашёл.
    """
    return {"message": f"Доступ с ключом: {current_api_key}", "items": ["item1", "item2"]}

Суть в чём, блядь:

  • Функция verify_api_key — это и есть зависимость. Она торчит, как суровый охранник, и требует api_key из заголовка.
  • Ключ не подошёл? Всё, приехали. HTTPException на 403 статусе — и свободен, иди нахуй.
  • В первом эндпоинте (/protected-route/) зависимость работает тихо, просто как проверка. Результат её нам не важен, главное — чтобы пропустила.
  • А во втором (/items/) мы этот самый проверенный ключ ловим в параметр current_api_key и можем с ним делать что хотим.

И это, сука, только верхушка айсберга! Зависимости могут быть асинхронными, классами, и их можно вкладывать друг в друга, как матрёшек — получается овердохуища гибкости.