Файлы конфигурации
Волга поддерживает файловую конфигурацию приложения. Вы можете описать настройки в TOML или JSON файле, привязать секции к строго типизированным Rust-структурам и получать к ним доступ в обработчиках запросов через экстрактор Config<T>. Также поддерживается горячая перезагрузка конфигурации.
Подготовка
Зависимости
Если вы не используете полный набор возможностей (full), вам необходимо включить config в Cargo.toml:
[dependencies]
volga = { version = "...", features = ["config"] }
serde = { version = "1", features = ["derive"] }
serde с фичей derive необходим для десериализации секций конфигурации в структуры.
Поддерживаемые форматы
Волга поддерживает два формата конфигурационных файлов (определяется по расширению):
- TOML (
.toml) — рекомендуемый - JSON (
.json)
Быстрый старт
Создайте файл app_config.toml в корне проекта:
[server]
host = "0.0.0.0"
port = 3000
[handler]
msg = "World"
Определите структуру для вашей секции и привяжите её при настройке приложения:
use volga::{App, Config, ok};
use serde::Deserialize;
#[derive(Deserialize)]
struct HandlerConfig {
msg: String,
}
#[tokio::main]
async fn main() -> std::io::Result<()> {
let mut app = App::new().with_config(|cfg| {
cfg.with_file("app_config.toml")
.bind_section::<HandlerConfig>("handler")
});
app.map_get("/hello", |cfg: Config<HandlerConfig>| async move {
ok!(fmt: "Hello, {}!", cfg.msg)
});
app.run().await
}
Теперь запрос GET /hello вернёт Hello, World!.
Загрузка конфигурации
Волга предоставляет три способа загрузки конфигурационного файла:
Конфигурация по умолчанию
Самый простой вариант — with_default_config() автоматически ищет app_config.toml или app_config.json в текущей рабочей директории:
let app = App::new().with_default_config();
Примечание
with_default_config() вызовет панику при запуске, если ни app_config.toml, ни app_config.json не найден.
Замыкание с билдером
Для полного контроля используйте with_config(), который предоставляет ConfigBuilder:
let app = App::new().with_config(|cfg| {
cfg.with_file("config/prod.toml")
.bind_section::<Database>("database")
.bind_section::<Cache>("cache")
.reload_on_change()
});
Если не вызывать with_file(), билдер будет искать файл по умолчанию — так же, как with_default_config(), то есть app_config.toml или app_config.json в текущей рабочей директории. Это удобно, когда нужен файл по умолчанию, но при этом требуется привязать пользовательские секции или включить горячую перезагрузку:
let app = App::new().with_config(|cfg| {
cfg.bind_section::<Database>("database")
.reload_on_change()
});
Отдельный билдер
Можно создать ConfigBuilder отдельно и передать его через set_config():
use volga::{App, ConfigBuilder};
let config = ConfigBuilder::from_file("config/prod.toml")
.bind_section::<Database>("database")
.reload_on_change();
let app = App::new().set_config(config);
Привязка секций
Обязательные секции
Используйте bind_section::<T>(key) для регистрации обязательной секции. Если секция отсутствует или имеет неверный формат, приложение вызовет панику при запуске:
#[derive(Deserialize)]
struct Database {
url: String,
}
let app = App::new().with_config(|cfg| {
cfg.with_file("app_config.toml")
.bind_section::<Database>("database")
});
Соответствующий app_config.toml:
[database]
url = "postgres://localhost/mydb"
Опциональные секции
Используйте bind_section_optional::<T>(key) для секций, которые могут отсутствовать. Если секция не найдена, Config<T> не будет доступен, но приложение не вызовет панику:
#[derive(Deserialize)]
struct Cache {
ttl: u64,
}
let app = App::new().with_config(|cfg| {
cfg.with_file("app_config.toml")
.bind_section_optional::<Cache>("cache")
});
Доступ к конфигурации в обработчиках
Используйте экстрактор Config<T> для доступа к привязанной секции в любом обработчике запросов. Он выполняет одну атомарную загрузку + Arc::clone на каждый запрос — без десериализации во время выполнения:
use volga::{App, Config, ok};
use serde::Deserialize;
#[derive(Deserialize)]
struct Database {
url: String,
}
#[tokio::main]
async fn main() -> std::io::Result<()> {
let mut app = App::new().with_config(|cfg| {
cfg.with_file("app_config.toml")
.bind_section::<Database>("database")
});
app.map_get("/db-url", |db: Config<Database>| async move {
ok!(db.url.as_str())
});
app.run().await
}
Встроенные секции
Волга автоматически распознаёт и применяет зарезервированные секции из конфигурационного файла. Эти секции настраивают внутренние компоненты фреймворка и не требуют вызова bind_section():
| Секция | Feature-флаг | Поля |
|---|---|---|
[server] | (всегда) | host, port, body_limit_bytes, max_header_count, max_connections |
[tls] | tls | Настройки TLS-сертификатов |
[tracing] | tracing | Настройки трассировки/логирования |
[openapi] | openapi | Настройки спецификации OpenAPI |
[cors] | middleware | Настройки политики CORS |
Например, секция [server] позволяет настроить хост и порт прямо в файле конфигурации:
[server]
host = "0.0.0.0"
port = 8080
body_limit_bytes = 1048576
max_connections = 1000
Совет
Встроенные секции применяются только при запуске. Горячая перезагрузка на них не влияет.
Горячая перезагрузка
Включите автоматическую перезагрузку конфигурации с помощью reload_on_change(). Волга будет опрашивать файл каждые 5 секунд и обновлять все привязанные секции:
let app = App::new().with_config(|cfg| {
cfg.with_file("app_config.toml")
.bind_section::<HandlerConfig>("handler")
.reload_on_change()
});
Поведение при перезагрузке:
- Обязательные секции — если обязательная секция исчезает из файла или становится некорректной, сохраняется предыдущее значение.
- Опциональные секции — если опциональная секция исчезает,
Config<T>становится недоступным. - Встроенные секции (server, tls и т.д.) не перезагружаются — они применяются только при запуске.
Пример с JSON
Вместо TOML можно использовать JSON. Создайте app_config.json:
{
"server": {
"host": "0.0.0.0",
"port": 3000
},
"database": {
"url": "postgres://localhost/mydb"
}
}
let app = App::new().with_config(|cfg| {
cfg.with_file("app_config.json")
.bind_section::<Database>("database")
});
Полный пример
Полный рабочий пример можно найти в репозитории Волги.