Внедрение Зависимостей
Волга поддерживает мощный механизм внедрения зависимостей (Dependency Injection, DI) с тремя жизненными циклами: Singleton, Scoped и Transient.
Если вы не используете функцию full
, то, включите функцию di
, либо переключитесь на full
в вашем Cargo.toml
:
[dependencies]
volga = { version = "0.4.5", features = ["di"] }
Жизненные циклы зависимостей
Singleton
Singleton обеспечивает создание и использование единственного экземпляра зависимости на протяжении всего жизненного цикла вашего веб-приложения. Этот экземпляр потокобезопасен и может использоваться одновременно в разных обработчиках.
Пример: Singleton
use volga::{App, di::Dc, ok, not_found};
use std::{
collections::HashMap,
sync::{Arc, Mutex},
};
#[derive(Clone, Default)]
struct InMemoryCache {
inner: Arc<Mutex<HashMap<String, String>>>,
}
#[tokio::main]
async fn main() -> std::io::Result<()> {
let mut app = App::new();
// Регистрация Singleton-сервиса
app.add_singleton(InMemoryCache::default());
// Использование общего экземпляра в обработчиках маршрутов
app.map_get("/user/{id}", |id: String, cache: Dc<InMemoryCache>| async move {
let user = cache.inner.lock().unwrap().get(&id);
match user {
Some(user) => ok!(user),
None => not_found!("Пользователь не найден"),
}
});
app.map_post("/user/{id}/{name}", |id: String, name: String, cache: Dc<InMemoryCache>| async move {
cache.inner.lock().unwrap().insert(id, name);
ok!()
});
app.run().await
}
В этом примере:
- Метод
add_singleton
регистрируетInMemoryCache
как Singleton. - Экстрактор
Dc<T>
разрешает зависимости по мере необходимости. Dc<T>
работает аналогично другим экстракторам, таким какJson<T>
илиQuery<T>
.
Инфо
Тип T
должен реализовывать типажи Send
, Sync
и Default
, если он не зависит от других объектов или используется готовый экземпляр.
Scoped
Scoped зависимость создает новый экземпляр для каждого HTTP-запроса. Экземпляр существует только в течение обработки запроса, обеспечивая изоляцию между запросами.
Пример: Scoped
use volga::{App, di::Dc, ok, not_found};
use std::{
collections::HashMap,
sync::{Arc, Mutex},
};
#[derive(Clone, Default)]
struct InMemoryCache {
inner: Arc<Mutex<HashMap<String, String>>>,
}
#[tokio::main]
async fn main() -> std::io::Result<()> {
let mut app = App::new();
// Регистрация Scoped-сервиса
app.add_scoped::<InMemoryCache>();
// Использование отдельного экземпляра для каждого запроса
app.map_get("/user/{id}", |id: String, cache: Dc<InMemoryCache>| async move {
let user = cache.inner.lock().unwrap().get(&id);
match user {
Some(user) => ok!(user),
None => not_found!("Пользователь не найден"),
}
});
app.map_post("/user/{id}/{name}", |id: String, name: String, cache: Dc<InMemoryCache>| async move {
cache.inner.lock().unwrap().insert(id, name);
ok!()
});
app.run().await
}
Основные отличия от Singleton:
- Метод
add_scoped::<T>()
регистрирует зависимость, которая создается для каждого HTTP-запроса. - Каждый запрос имеет свой уникальный экземпляр
InMemoryCache
.
Transient
Transient зависимость создает новый экземпляр при каждом запросе к контейнеру, независимо от контекста или запроса. Регистрация осуществляется с помощью метода add_transient::<T>()
. Поведение похоже на Scoped, но экземпляр создается при каждом внедрении зависимости.
Совет
Реализуя Default
вручную, вы можете управлять поведением создания экземпляров для Scoped и Transient зависимостей, а для более сложных сценариев используйте типаж Inject
.
Использование DI в middleware
Чтобы внедрить зависимость в middleware, используйте метод resolve::<T>()
, либо resolve_shared::<T>
структуры HttpContext
. Основное различие между ними заключается в том, что первый метод требует реализации типажа Clone
для T
, тогда как последний просто возвращает Arc<T>
.
app.use_middleware(|ctx: HttpContext, next: Next| async move {
let cache = ctx.resolve::<InMemoryCache>()?;
// Выполнить действия...
next(ctx).await
});
Итог
- Singleton: Общий экземпляр на весь жизненный цикл приложения.
- Scoped: Новый экземпляр для каждого HTTP-запроса.
- Transient: Новый экземпляр при каждом запросе к контейнеру.
Более сложные примеры можно найти здесь.