Внедрение зависимостей в FastAPI

FastAPI предоставляет мощную и лаконичную систему внедрения зависимостей.

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


Что такое внедрение зависимостей

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

Пример

from typing import Annotated
from fastapi import Depends, FastAPI

app = FastAPI()


# 1. Определите функцию зависимости
def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
    return {"q": q, "skip": skip, "limit": limit}


# 2. Использование зависимостей в маршрутах
@app.get("/items/")
async def read_items(commons: dict = Depends(common_parameters)):
    # commons получает возвращаемое значение функции зависимости
    return commons


@app.get("/users/")
async def read_users(commons: dict = Depends(common_parameters)):
    # Несколько маршрутов могут использовать одну и ту же зависимость
    return commons

Пояснение к коду:

ЧастьОписание
common_parametersФункции зависимостей, обрабатывающие общие параметры запроса
Depends(common_parameters)Объявите параметрыcommonsЗначение поступает из функции зависимости
commons: dictПолучает словарь, возвращаемый функцией зависимости

Функции зависимостей и функции операций пути используют один и тот же стиль объявления параметров, и FastAPI автоматически разрешает параметры функции зависимости. Это означает, что функции зависимостей также могут использовать параметры запроса, параметры пути, тело запроса и т. д.


Поток выполнения зависимостей

Когда приходит запрос, порядок обработки FastAPI:

  1. Определить функции маршрута и их зависимости
  2. Выполнить функции зависимостей (в порядке зависимостей)
  3. Передать возвращаемое значение функции зависимости в функцию маршрута.
  4. Выполнить функцию маршрута

Класс как зависимость

Помимо функций, вы также можете использовать классы в качестве зависимостей:

Пример

from typing import Annotated
from fastapi import Depends, FastAPI

app = FastAPI()


# Объявление зависимостей с помощью классов
class CommonQueryParams:
    def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
        self.q = q
        self.skip = skip
        self.limit = limit


# Использовать класс в качестве зависимости
@app.get("/items/")
async def read_items(commons: Annotated[CommonQueryParams, Depends()]):
    return {"q": commons.q, "skip": commons.skip, "limit": commons.limit}

КогдаDepends()Когда параметр не передан, FastAPI автоматически использует аннотацию типа параметра (CommonQueryParams) в качестве зависимости.


подзависимость

Зависимости могут иметь свои собственные зависимости, образуя цепочку зависимостей:

Пример

from typing import Annotated
from fastapi import Depends, FastAPI

app = FastAPI()


# Функции зависимостей
def query_extractor(q: str | None = None):
    return q


# Подзависимость: зависит от query_extractor.
def query_checker(q: str = Depends(query_extractor)):
    if q == "admin":
        # Подзависимости можно проверять
        return q + " (checked)"
    return q


# Маршруты используют подзависимости
@app.get("/items/")
async def read_items(q: str = Depends(query_checker)):
    return {"q": q}

Поток выполнения:query_extractor -> query_checker-> функция маршрута


Использование зависимостей в декораторах

Иногда вам нужны только побочные эффекты зависимости (например, проверки разрешений), а не её возвращаемое значение. В декоратореdependenciesОбъявлено в параметрах:

Пример

from fastapi import Depends, FastAPI, Header, HTTPException

app = FastAPI()


# Зависимость: проверка API-ключа
async def verify_api_key(x_api_key: str = Header()):
    if x_api_key != "secret-key":
        raise HTTPException(status_code=400, detail="X-API-Key invalid")


# Использование зависимости в декораторе, возвращаемое значение не требуется
@app.get("/items/", dependencies=[Depends(verify_api_key)])
async def read_items():
    return [{"item": "Foo"}]


# Применить зависимость ко всей группе маршрутов
@app.get("/users/", dependencies=[Depends(verify_api_key)])
async def read_users():
    return [{"user": "Bar"}]

Глобальные зависимости

Вы можетеFastAPIОбъявите глобальные зависимости на экземпляре, действующие для всех маршрутов:

Пример

from fastapi import Depends, FastAPI, Header, HTTPException

async def verify_token(x_token: str = Header()):
    if x_token != "fake-super-secret-token":
        raise HTTPException(status_code=400, detail="Неверный заголовок X-Token")


# Глобальная зависимость: все маршруты должны проходить проверку токена
app = FastAPI(dependencies=[Depends(verify_token)])


@app.get("/items/")
async def read_items():
    return [{"item": "Foo"}]


@app.get("/users/")
async def read_users():
    return [{"user": "Bar"}]

Зависимости с использованием yield

Когда зависимость должна выполнить операции очистки (например, закрытие соединений с базой данных), используйтеyield:

Пример

from typing import Annotated
from fastapi import Depends, FastAPI

app = FastAPI()


# Зависимости, использующие yield: создайте соединение до запроса и закройте его после запроса.
def get_db():
    db = "database_connection"  # Имитация создания соединения с базой данных
    try:
        yield db  # Предоставить подключение к базе данных во время обработки запроса
    finally:
        print("Close database connection")  # Освободить ресурсы после завершения запроса


@app.get("/items/")
async def read_items(db: str = Depends(get_db)):
    return {"db": db}

Поток выполнения:

  1. При поступлении запроса выполняетсяyieldКод до этого, создающий подключение к базе данных
  2. yieldПередайте подключение в функцию маршрута
  3. После завершения функции маршрута выполняетсяfinallyКод очистки в блоке

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


Итог

  • Внедрение зависимостей выделяет общую логику в переиспользуемые функции или классы.
  • ИспользованиеDepends()Объявите зависимости в параметрах функции маршрута
  • Зависимости могут быть вложенными, образуя цепочки зависимостей
  • декоратораdependenciesПараметры, используемые для зависимостей, которым не нужны возвращаемые значения
  • ИспользованиеyieldЗависимости поддерживают очистку ресурсов
  • Глобальные зависимости применяются ко всем маршрутам
Другие расширения