Вопрос или проблема
Мы используем Azure Key Vault, который хранит секреты (например, пароли). Мы реализовали клиент под названием KeyVaultClient
, который запрашивает необходимые секреты из Azure Key Vault. В Key Vault есть один секрет, который требуется во время конфигурации в нашем классе Startup.cs. Вот код:
public class Startup
{
[...]
public void ConfigureServices(IServiceCollection services)
{
[...]
// регистрация DI и зависимой конфигурации
services.AddSingleton<IKeyVaultClient, KeyVaultClient>().Configure<KeyVaultClientSettings>(Configuration.GetSection(KeyVaultClientSettings.Section));
// код, который разрешает IKeyVaultClient, потому что нам нужно запросить секрет
var serviceProvider = services.BuildServiceProvider();
var keyVaultClient = serviceProvider.GetService<IKeyVaultClient>();
var secretName = "Имя моего секрета";
var secret = keyVaultClient.GetSecret(secretName);
// Наш собственный метод расширения, который аутентифицирует это приложение по секрету
services.AddFrontendAuthentication(secret);
[...]
}
}
Регистрация IKeyVaultClient для DI и его непосредственное разрешение выглядит как-то неаккуратно. Каковы лучшие практики, чтобы сделать код более чистым и переиспользуемым в других проектах?
Ответ или решение
Для обеспечения чистоты и повторного использования кода при регистрации зависимостей в методе ConfigureServices
, рекомендуется использовать подход, который минимизирует необходимость в построении провайдера сервисов вручную и обеспечивает более чистую и структурированную архитектуру.
Шаг 1: Использование фабрики для получения секрета
Один из способов улучшить код — это создать фабричный класс для получения секрета из KeyVaultClient
. Таким образом, вы сможете инкапсулировать логику получения секрета и сделать ее переиспользуемой.
public interface ISecretProvider
{
string GetSecret(string secretName);
}
public class SecretProvider : ISecretProvider
{
private readonly IKeyVaultClient _keyVaultClient;
public SecretProvider(IKeyVaultClient keyVaultClient)
{
_keyVaultClient = keyVaultClient;
}
public string GetSecret(string secretName)
{
return _keyVaultClient.GetSecret(secretName);
}
}
Шаг 2: Регистрация зависимостей
Теперь в классе Startup
вы можете зарегистрировать ISecretProvider
, используя IKeyVaultClient
. Этот процесс будет выглядеть гораздо более чисто.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IKeyVaultClient, KeyVaultClient>()
.Configure<KeyVaultClientSettings>(Configuration.GetSection(KeyVaultClientSettings.Section));
// Регистрация SecretProvider
services.AddSingleton<ISecretProvider, SecretProvider>();
// Использование ISecretProvider для получения секрета
var serviceProvider = services.BuildServiceProvider();
var secretProvider = serviceProvider.GetService<ISecretProvider>();
var secretName = "My Secret Name";
var secret = secretProvider.GetSecret(secretName);
// Регистрация аутентификации с использованием секрета
services.AddFrontendAuthentication(secret);
}
}
Причины, почему это лучшее решение:
- Чистота кода: Логика получения секрета инкапсулирована в одном классе, что делает код в
Startup
более читабельным и упрощает его понимание. - Повторное использование: Вы можете использовать
SecretProvider
в других местах вашего приложения без дублирования кода. - Тестирование: Легче писать модульные тесты для
SecretProvider
, так как вы можете замокироватьIKeyVaultClient
и проверять, как ваш код реагирует на различные сценарии получения секрета.
Заключение
Создание фабрики для получения секретов и использование её в ConfigureServices
улучшает чистоту, тестируемость и повторное использование вашего кода. Это практическое решение поможет сделать ваше приложение более устойчивым и гибким. Вы также сможете легко расширять данный подход для добавления новых зависимостей или функциональности в будущем.