Как лучше настроить приложения C# и .NET для SendGrid

Эта статья была написана для компании Twilio и первоначально опубликована в блоге Twilio.

Существует сотня различных способов предоставить конфигурацию вашим приложениям. Почти для любого языка программирования вы можете использовать переменные окружения и файлы .env, но конфигурация также может храниться в других форматах файлов, таких как JSON, YAML, TOML, XML, INI, и список можно продолжать. Однако в некоторых сценариях конфигурация не извлекается из файлов, а вместо этого извлекается из Azure Key Vault, HashiCorp Vault или аналогичной службы хранилища. Редко бывает так, что конфигурация поступает из одного источника. К счастью, в .NET есть API, которые помогут вам получить конфигурацию из нескольких источников и объединить их вместе.

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

  • получать конфигурацию из нескольких источников, в частности, из JSON-файлов, пользовательских секретов, переменных окружения и аргументов командной строки
  • связывать конфигурацию с сильно типизированными объектами
  • внедрять конфигурацию с помощью инъекции зависимостей, следуя шаблону опций.

Предварительные условия

Для изучения этого руководства вам понадобятся следующие вещи:

  • ОС, поддерживающая .NET (Windows/macOS/Linux)
  • Git CLI
  • .NET 6 SDK
  • Редактор кода или IDE (я рекомендую VS Code с плагином C#, Visual Studio или JetBrains Rider)
  • Бесплатный или платный аккаунт Twilio (если вы зарегистрируетесь здесь, то получите $10 в кредит Twilio при переходе на платный аккаунт!)
  • Учетная запись Twilio SendGrid. Зарегистрируйтесь здесь, чтобы отправлять до 100 писем в день совершенно бесплатно.

Исходный код этого руководства вы можете найти на GitHub. Используйте его, если у вас возникнут какие-либо проблемы, или отправьте вопрос, если у вас возникнут проблемы.

Настройка аккаунта SendGrid для отправки электронных писем

Перед тем, как вы сможете отправлять электронные письма, необходимо настроить две вещи. Во-первых, вам нужно настроить аутентификацию отправителя. Это позволит убедиться, что вы являетесь владельцем адреса электронной почты или домена, с которого вы будете отправлять электронные письма. Во-вторых, вам нужно будет создать API-ключ SendGrid с разрешением на отправку писем.

Аутентификация отправителя

Чтобы быстро начать работу, в этом руководстве вы будете использовать проверку одного отправителя. Это подтверждает, что вы являетесь владельцем адреса электронной почты, с которого приложение будет отправлять электронные письма. Single Sender Verification отлично подходит для тестирования, но не рекомендуется для производства.

Twilio рекомендует использовать аутентификацию домена в производственных средах. Аутентифицированный домен доказывает поставщикам электронной почты, что вы являетесь владельцем домена, и удаляет текст «via sendgrid.net», который в противном случае поставщики добавили бы к вашему адресу from.

Чтобы настроить проверку единого отправителя, перейдите на вкладку Настройки в левом меню. Когда откроется вкладка настроек, нажмите Аутентификация отправителя.

Затем нажмите кнопку Начать работу в разделе Проверка одного отправителя.

Откроется форма в правой части панели. Заполните форму, указав свои данные и адрес электронной почты.

После заполнения формы нажмите Создать. Справа появится еще одна панель с просьбой подтвердить адрес электронной почты в вашем почтовом ящике. Перейдите в ваш личный почтовый ящик, откройте письмо от SendGrid и нажмите Проверить единого отправителя.

Ваш адрес электронной почты был подтвержден. Теперь вы можете использовать его для отправки писем!

Создание API-ключа SendGrid для отправки электронных писем

Вернувшись на сайт SendGrid, нажмите API Keys на вкладке Settings, затем нажмите Create API Key в правом верхнем углу. Откроется еще одна форма в правой панели.

Дайте своему API-ключу осмысленное имя. Вы можете назначить различные разрешения для ключа API. Для оптимальной безопасности следует назначать только минимальное количество разрешений, которое вам необходимо.

Далее нажмите Ограниченный доступ.

Прокрутите вниз до элемента аккордеона Mail Send и щелкните по нему, чтобы открыть расположенные под ним разрешения. Перетащите ползунок вправо для разрешения Mail Send.

Прокрутите форму в самый низ и нажмите кнопку Создать и просмотреть. Теперь ключ API будет отображаться на вашем экране.

Вы не сможете получить API-ключ, как только покинете этот экран, поэтому обязательно скопируйте API-ключ в безопасное место.

После проверки отправителя и создания API-ключа вы можете приступать к написанию кода!

Для всех последующих команд и кода замените следующие символы следующим образом:

  • Замените [SENDGRID_API_KEY] на API-ключ, который вы только что записали.
  • Замените [FROM_EMAIL_ADDRESS] и [FROM_NAME] адресом электронной почты, который вы проверили ранее, и именем, которое вы хотите видеть у получателей.
  • Замените [TO_EMAIL_ADDRESS] и [TO_NAME] на адрес электронной почты и имя желаемого получателя письма.

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

Чтобы быстро запустить проект на вашей машине, откройте предпочитаемую оболочку и используйте следующую команду git для клонирования исходного кода:

git clone https://github.com/Swimburger/SendGridOptionsPattern.git --branch Step1
Вход в полноэкранный режим Выйдите из полноэкранного режима

Затем перейдите в папку SendGridOptionsPattern, используя cd SendGridOptionsPattern. Этот проект представляет собой консольное приложение с одним файлом C#, Program.cs, который содержит следующий код:

using SendGrid;
using SendGrid.Helpers.Mail;

var apiKey = Environment.GetEnvironmentVariable("SendGridApiKey");
var fromEmailAddress = Environment.GetEnvironmentVariable("FromEmailAddress");
var fromName = Environment.GetEnvironmentVariable("FromName");
var toEmailAddress = Environment.GetEnvironmentVariable("ToEmailAddress");
var toName = Environment.GetEnvironmentVariable("ToName");

var client = new SendGridClient(apiKey);
var message = new SendGridMessage
{
    From = new EmailAddress(fromEmailAddress, fromName),
    Subject = "Sending with Twilio SendGrid is Fun",
    PlainTextContent = "and easy to do anywhere, especially with C#"
};
message.AddTo(new EmailAddress(toEmailAddress, toName));
var response = await client.SendEmailAsync(message);

// A success status code means SendGrid received the email request and will process it.
// Errors can still occur when SendGrid tries to send the email. 
// If email is not received, use this URL to debug: https://app.sendgrid.com/email_activity 
Console.WriteLine(response.IsSuccessStatusCode ? "Email queued successfully!" : "Something went wrong!");
Вход в полноэкранный режим Выйти из полноэкранного режима

Приложение извлекает API-ключ SendGrid, а также адрес электронной почты и имя для отправителя и получателя из переменных окружения. Затем код создает новый SendGridClient с ключом API, создает сообщение электронной почты с помощью SendGridMessage, а затем отправляет письмо.

Приложение использует библиотеку SendGrid для C# и .NET для аутентификации и отправки электронной почты через SendGrid API. Пакет SendGrid NuGet был добавлен в проект как часть исходного кода, который вы клонировали. Вы можете найти эту зависимость в csproj-файле.

Прежде чем запускать это приложение, необходимо настроить переменные окружения, от которых зависит проект. Вернувшись в оболочку, установите эти переменные окружения с помощью следующих команд.

Если вы используете Bash или аналогичную оболочку:

export SendGridApiKey=[SENDGRID_API_KEY]
export FromEmailAddress=[FROM_EMAIL_ADDRESS]
export FromName="[FROM_NAME]"
export ToEmailAddress=[TO_EMAIL_ADDRESS]
export ToName="[TO_NAME]"
Войти в полноэкранный режим Выйти из полноэкранного режима

Если вы используете PowerShell:

$Env:SendGridApiKey = "[SENDGRID_API_KEY]"
$Env:FromEmailAddress = "[FROM_EMAIL_ADDRESS]"
$Env:FromName = "[FROM_NAME]"
$Env:ToEmailAddress = "TO_EMAIL_ADDRESS]"
$Env:ToName = "[TO_NAME]"
Войдите в полноэкранный режим Выйти из полноэкранного режима

Если вы используете CMD:

set "SendGridApiKey=[SENDGRID_API_KEY]"
set "FromEmailAddress=[FROM_EMAIL_ADDRESS]"
set "FromName=[FROM_NAME]"
set "ToEmailAddress=[TO_EMAIL_ADDRESS]"
set "ToName=[TO_NAME]"
Войдите в полноэкранный режим Выйти из полноэкранного режима

После установки переменных окружения можно запустить проект с помощью следующей команды .NET CLI:

dotnet run
Enter fullscreen mode Выйти из полноэкранного режима

Приложение отправит письмо с вашего проверенного адреса электронной почты получателю.

Когда SendGrid API отвечает успешным кодом состояния HTTP, это не обязательно означает, что письмо было успешно получено получателем. Однако это означает, что SendGrid API принял ваш запрос и отправит письмо. Если получатель не получил письмо, вы можете использовать Email Activity Feed для выяснения причины.

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

Многие из этих элементов конфигурации, особенно API Key, являются конфиденциальными секретами, которыми вы не хотите делиться с другими. Извлекая их из внешних конфигураций, таких как переменные окружения, вы гарантируете, что случайно не занесете их в свой публичный исходный код, чтобы все увидели. И когда кто-то другой захочет запустить тот же код, он может настроить свою собственную конфигурацию ключа API в переменных окружения и запустить его.

Вот почему в примерах SendGrid мы всегда будем использовать переменные окружения. Хотя это дополнительный шаг по сравнению с жестким кодированием конфигурации, это самый быстрый способ заставить вас работать с SendGrid без случайной компрометации вашего API-ключа.

Однако в .NET есть лучший способ получения внешней конфигурации, используя ConfigurationBuilder.

Построение конфигурации с помощью конструктора конфигурации .NET

Существует множество способов конфигурирования приложений .NET, и обычно конфигурация составляется из нескольких источников. В ASP.NET Core изначально были представлены API, позволяющие легко загружать конфигурацию из множества различных источников с помощью провайдеров конфигурации и объединять их вместе с помощью ConfigurationBuilder. Эти API были перенесены в пакет Microsoft.Extensions.Hosting NuGet, чтобы их можно было использовать в любом приложении .NET.

Вы можете использовать новые API конфигурации не только в .NET (Core) — вы можете использовать их даже в .NET Framework. В .NET Framework есть свои собственные API конфигурации, но они менее интуитивны и устарели по сравнению с новыми API конфигурации.

Вернувшись в оболочку, выполните следующие команды .NET CLI, чтобы добавить пакеты NuGet для расширений конфигурации и некоторых поставщиков конфигурации:

dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.Configuration.CommandLine
dotnet add package Microsoft.Extensions.Configuration.EnvironmentVariables
dotnet add package Microsoft.Extensions.Configuration.UserSecrets
Войти в полноэкранный режим Выйти из полноэкранного режима
  • Пакет Microsoft.Extensions.Configuration содержит основные API для создания конфигурации, а также провайдер конфигурации JSON.
  • Пакет Microsoft.Extensions.Configuration.CommandLine добавляет поставщик конфигурации, который создает конфигурацию из аргументов командной строки.
  • Пакет Microsoft.Extensions.Configuration.EnvironmentVariables добавляет поставщик конфигурации, который создает конфигурацию на основе переменных окружения.
  • Пакет Microsoft.Extensions.Configuration.UserSecrets добавляет поставщик конфигурации, который создает конфигурацию из менеджера секретов.

Чтобы начать использовать API-интерфейсы конфигурации, обновите файл Program.cs следующим кодом:

using System.Reflection;
using Microsoft.Extensions.Configuration;
using SendGrid;
using SendGrid.Helpers.Mail;

IConfiguration config = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
    .AddUserSecrets(Assembly.GetExecutingAssembly(), optional: true, reloadOnChange: false)
    .AddEnvironmentVariables()
    .AddCommandLine(args)
    .Build();

var apiKey = config["SendGrid:ApiKey"];
var fromEmailAddress = config["Email:From:Email"];
var fromName = config["Email:From:Name"];
var toEmailAddress = config["Email:To:Email"];
var toName = config["Email:To:Name"];
var emailSubject = config["Email:Subject"];
var emailBody = config["Email:Body"];

var client = new SendGridClient(apiKey);
var message = new SendGridMessage
{
    From = new EmailAddress(fromEmailAddress, fromName),
    Subject = emailSubject,
    PlainTextContent = emailBody
};
message.AddTo(new EmailAddress(toEmailAddress, toName));
var response = await client.SendEmailAsync(message);

// A success status code means SendGrid received the email request and will process it.
// Errors can still occur when SendGrid tries to send the email. 
// If email is not received, use this URL to debug: https://app.sendgrid.com/email_activity 
Console.WriteLine(response.IsSuccessStatusCode ? "Email queued successfully!" : "Something went wrong!");
Вход в полноэкранный режим Выход из полноэкранного режима

В результате приложение выглядит так же, как и раньше, но теперь конфигурация загружается из нескольких источников, а не напрямую из переменных окружения. ConfigurationBuilder — это класс, который использует шаблон конструктора, чтобы вы могли объединить в цепочку несколько поставщиков конфигурации с помощью методов расширения Add.... Когда элементы конфигурации, поступающие от разных провайдеров, имеют одинаковый ключ, конфигурация, добавленная позже в цепочку, перезапишет конфигурацию, добавленную раньше в цепочке. Это означает, что порядок добавления провайдеров конфигурации имеет значение! В данном случае:

  • User Secrets перезаписывает конфигурацию из JSON
  • Переменные среды перезаписывают конфигурацию из User Secrets и JSON
  • Command-Line Arguments перезаписывает конфигурацию из User Secrets, JSON и Environment Variables.

Параметр optional указывает, должно ли приложение выдавать исключение, если базовый источник отсутствует. Параметр reloadOnChange указывает, обновлять или нет конфигурацию при изменении базового источника. Например, если в вашем производственном приложении есть проблема, но по причинам производительности уровень логирования установлен на Warning в appsettings.json, вы можете изменить уровень логирования на Debug. После сохранения файла приложение начнет писать журналы от уровня Debug и выше, без необходимости перезапуска приложения!

После сборки конфигурации приложение возьмет из нее элементы конфигурации, но обратите внимание, что ключи конфигурации теперь используют два разных префикса: SendGrid: и Email:.

Вы можете организовать свою конфигурацию в иерархические секции, добавив двоеточия : в качестве разделителей. Таким образом, в данном случае ApiKey является частью раздела SendGrid, а вся остальная конфигурация является частью раздела Email. Вы также можете вложить разделы, как показано в разделе Email. Раздел Email содержит дочерние разделы From и To, которые хранят адреса электронной почты и имена отправителя и получателя. Существует множество различных способов структурирования конфигурации, но то, как вы организуете конфигурацию, зависит от ваших потребностей.

Теперь, когда приложение загружает конфигурацию из нескольких источников, в каком источнике следует хранить конфигурацию? Это зависит от конфигурации, вашего сценария использования и личных предпочтений:

  • Используйте JSON для конфигурации, не содержащей секретов или другой конфиденциальной информации.
  • Используйте User Secrets только для локальной разработки для конфигурации секретов и другой конфиденциальной информации.
  • Использовать переменные среды для конфигурации, специфичной для среды, включая секреты или другую конфиденциальную информацию. Однако для локальной разработки предпочтительнее использовать User Secrets. Переменные окружения являются мощным источником конфигурации, поскольку все операционные системы, контейнерные технологии и облачная инфраструктура поддерживают их.
  • Используйте аргументы командной строки для настройки специфических параметров выполнения.

Создайте новый файл appsettings.json и добавьте в него следующий JSON:

{
  "Email": {
    "From": {
      "Email": "[FROM_EMAIL_ADDRESS]",
      "Name": "[FROM_NAME]"
    },
    "Subject": "Ahoy from JSON!",
    "Body": "Thank you for signing up to our website. See you around!"
  }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Файл appsettings.json предоставит конфигурацию FromEmailAddress, FromName, Subject, и Body, хотя также имеет смысл передать эту конфигурацию в качестве аргументов командной строки.

По умолчанию в консольном приложении файл appsettings.json не будет скопирован в вывод проекта. В результате поставщик конфигурации не найдет базовый источник, что означает, что конфигурация в вашем JSON не будет загружена. Чтобы убедиться, что файл appsettings.json будет скопирован на выход, добавьте узел Content в csproj-файл, как показано ниже:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>...</PropertyGroup>
  <ItemGroup>...</ItemGroup>

  <ItemGroup>
    <Content Include="appsettings.json" CopyToOutputDirectory="Always" />
  </ItemGroup>
</Project>
Вход в полноэкранный режим Выйти из полноэкранного режима

Чтобы начать использовать Secret Manager, сначала необходимо инициализировать его для вашего проекта с помощью следующей команды:

dotnet user-secrets init
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь вы можете установить конфигурацию SendGrid:ApiKey следующим образом:

dotnet user-secrets set SendGrid:ApiKey [SENDGRID_API_KEY]
Войти в полноэкранный режим Выйти из полноэкранного режима

Вполне вероятно, что вы настроите API Key как переменную окружения в других средах, но поскольку вы запускаете это локально, то предпочтительнее использовать Secret Manager. Поэтому в данном случае вам не нужно настраивать переменные окружения.

Теперь приложение имеет конфигурацию для того, от кого будет отправлено письмо, но не для того, кому оно будет отправлено. Вместо того чтобы настраивать его с помощью одного из предыдущих источников, запустите проект с помощью .NET CLI и передайте его в качестве аргумента командной строки:

dotnet run --Email:To:Email [TO_EMAIL_ADDRESS] --Email:To:Name "[TO_NAME]"
Войти в полноэкранный режим Выйти из полноэкранного режима

Как и раньше, получатель получит письмо, но теперь его темой будет «Ahoy from JSON!».

Выполните команду снова, но перезапишите конфигурацию Email:Subject с помощью аргумента командной строки:

dotnet run --Email:To:Email [TO_EMAIL_ADDRESS] --Email:To:Name "[TO_NAME]" --Email:Subject "Ahoy from the CLI"
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь получатель получит письмо с темой «Ahoy from the CLI».

Конечный результат может быть тем же самым, но ваше приложение теперь гораздо более гибкое, поскольку его можно настраивать различными способами. Однако это не единственные поставщики конфигурации. У Microsoft есть еще 5 поставщиков конфигурации, и если они не удовлетворяют вашим потребностям, вы можете разработать свой собственный поставщик или использовать чужой.

Некоторые операционные системы и оболочки могут не позволять использовать двоеточия. В этом случае вы можете использовать двойное подчеркивание __ в качестве разделителя, и .NET заменит его двоеточием.

Привязка конфигурации к сильно типизированным объектам

Вы только что узнали, как использовать конструктор конфигурации для получения конфигурации из нескольких источников, но количество конфигурации может быстро выйти из-под контроля. Более удобным решением этой проблемы является привязка конфигурации к сильно типизированным объектам.

Чтобы начать связывание конфигурации, добавьте пакет Microsoft.Extensions.Configuration.Binder NuGet с помощью следующей команды:

dotnet add package Microsoft.Extensions.Configuration.Binder
Войти в полноэкранный режим Выйти из полноэкранного режима

Обновите файл Program.cs с помощью приведенного ниже кода:

using System.Reflection;
using Microsoft.Extensions.Configuration;
using SendGrid;
using SendGrid.Helpers.Mail;

IConfiguration config = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
    .AddUserSecrets(Assembly.GetExecutingAssembly(), optional: true, reloadOnChange: false)
    .AddEnvironmentVariables()
    .AddCommandLine(args)
    .Build();

var apiKey = config["SendGrid:ApiKey"];

var emailOptions = config.GetSection("Email").Get<EmailOptions>();

var client = new SendGridClient(apiKey);
var message = new SendGridMessage
{
    From = emailOptions.From,
    Subject = emailOptions.Subject,
    PlainTextContent = emailOptions.Body
};
message.AddTo(emailOptions.To);
var response = await client.SendEmailAsync(message);

// A success status code means SendGrid received the email request and will process it.
// Errors can still occur when SendGrid tries to send the email. 
// If email is not received, use this URL to debug: https://app.sendgrid.com/email_activity 
Console.WriteLine(response.IsSuccessStatusCode ? "Email queued successfully!" : "Something went wrong!");

public class EmailOptions
{
    public EmailAddress From { get; set; }
    public EmailAddress To { get; set; }
    public string Subject { get; set; }
    public string Body { get; set; }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

В нижней части Program.cs есть новый класс, который имеет свойства с именами, соответствующими вашей конфигурации. Сопоставив имена свойств и ключи конфигурации, вы сможете привязать конфигурацию к экземплярам указанного класса. Вы можете получить секции из конфигурации, используя config.GetSection("YourSectionName"), а затем привязать секцию к сильно типизированным объектам, используя .Get<YourClass>(). Таким образом, секция Email привязывается к экземплярам EmailOptions, которые затем используются для отправки письма, как и раньше.

Для класса EmailOptions свойства From и To определяются с помощью класса EmailAddress, предоставляемого библиотекой SendGrid. Класс EmailAddress содержит два свойства: Email и Name. Поскольку имена этих свойств совпадают с именами вашей конфигурации, даже эти вложенные объекты будут связаны.

Если вы запустите проект сейчас, вы продолжите получать тот же результат:

dotnet run --Email:To:Email [TO_EMAIL_ADDRESS] --Email:To:Name "[TO_NAME]" --Email:Subject "Ahoy from the CLI"

Вход в полноэкранный режим Выход из полноэкранного режима

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

Замена построителя конфигурации на построитель хоста по умолчанию

Если вы использовали шаблоны ASP.NET, вы можете заметить, что конфигурация уже настроена без какого-либо кода ConfigurationBuilder. Это потому, что шаблоны ASP.NET используют Web Host по умолчанию, который устанавливает конфигурацию по умолчанию, логирование, внедрение зависимостей, функциональность, связанную с веб, и многое другое.

Если вам нужна та же конфигурация, протоколирование и внедрение зависимостей из конструктора веб-хоста, но без функциональности, связанной с веб, вы можете использовать Generic Host вместо Web Host.

Добавьте пакет Microsoft.Extensions.Hosting NuGet с помощью .NET CLI:

dotnet add package Microsoft.Extensions.Hosting
Войдите в полноэкранный режим Выйти из полноэкранного режима

В файле Program.cs замените утверждения using и код ConfigurationBuilder следующим кодом:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using SendGrid;
using SendGrid.Helpers.Mail;

using IHost host = Host.CreateDefaultBuilder(args).Build();
var config = host.Services.GetRequiredService<IConfiguration>();
Вход в полноэкранный режим Выйти из полноэкранного режима

Вместо того чтобы создавать конфигурацию самостоятельно, проект теперь использует значения по умолчанию, поставляемые с Generic Host. Затем код получает конфигурацию из контейнера внедрения зависимостей (host.Services) и продолжает использовать объект конфигурации, как и раньше.

По умолчанию Generic Host будет собирать конфигурацию аналогично тому, как вы делали это сами, но есть и некоторые различия. Обзор стандартных настроек Generic Host можно найти в документации Microsoft.

Стандартный Generic Host строит конфигурацию несколько иначе. Более конкретно, ваши пользовательские секреты будут загружены, только если Environment настроено как Development. Существует несколько способов настройки окружения, но для данного руководства передавайте аргумент Environment при запуске проекта, как показано ниже:

dotnet run --Environment Development  --Email:To:Email [TO_EMAIL_ADDRESS] --Email:To:Name "[TO_NAME]" --Email:Subject "Ahoy from the CLI"
Войти в полноэкранный режим Выйти из полноэкранного режима

Получение конфигурации из инъекции зависимостей с помощью шаблона options

Теперь, когда вы используете Generic Host, вы можете начать использовать инъекцию зависимостей (DI), которая поставляется вместе с ним, точно так же, как и в приложениях ASP.NET.

Замените существующий код в Program.cs следующим кодом:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using SendGrid;
using SendGrid.Helpers.Mail;

using IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices((context, services) =>
    {
        services.Configure<EmailOptions>(context.Configuration.GetSection("Email"));
        services.AddTransient<EmailSender>();
    })
    .Build();

var config = host.Services.GetRequiredService<IConfiguration>();

var apiKey = config["SendGrid:ApiKey"];
var client = new SendGridClient(apiKey);

var emailSender = host.Services.GetRequiredService<EmailSender>();
await emailSender.SendEmail(client);

public class EmailSender
{
    private readonly EmailOptions emailOptions;

    public EmailSender(IOptions<EmailOptions> emailOptions)
    {
        this.emailOptions = emailOptions.Value;
    }

    public async Task SendEmail(ISendGridClient client)
    {
        var message = new SendGridMessage
        {
            From = emailOptions.From,
            Subject = emailOptions.Subject,
            PlainTextContent = emailOptions.Body
        };
        message.AddTo(emailOptions.To);
        var response = await client.SendEmailAsync(message);

        // A success status code means SendGrid received the email request and will process it.
        // Errors can still occur when SendGrid tries to send the email. 
        // If email is not received, use this URL to debug: https://app.sendgrid.com/email_activity 
        Console.WriteLine(response.IsSuccessStatusCode ? "Email queued successfully!" : "Something went wrong!");
    }
}

public class EmailOptions
{
    public EmailAddress From { get; set; }
    public EmailAddress To { get; set; }
    public string Subject { get; set; }
    public string Body { get; set; }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Вы можете использовать метод ConfigureServices на конструкторе хоста и передать в нем лямбду для добавления дополнительных сервисов в контейнер DI. В этой лямбде раздел конфигурации Email добавляется как конфигурация с помощью services.Configure<YourOptions>. В результате ваша конфигурация будет внедрена везде, где поддерживается DI, путем добавления параметра типа IOptions<YourOptions> в конструктор или сигнатуру метода. Microsoft называет это шаблоном Options.

Помимо IOptions<T>, существуют также IOptionsSnapshot<T>, IOptionsMonitor<T> и IOptionsFactory<TOptions>, которые имеют различное поведение и случаи использования. Подробнее о различиях между этими интерфейсами опций можно узнать в документации Microsoft.

После настройки опций программа добавит класс EmailSender в контейнер CI. EmailSender — это новый класс, который будет отвечать за отправку электронных писем. Теперь, когда контейнер DI создан, вы можете получить из него сервисы. Итак, теперь у вас есть ваш EmailOptions, внедренный в любое место, поддерживающее DI.

Теперь код получит из контейнера DI экземпляр EmailSender, который используется для отправки электронного письма с помощью метода SendEmail.

Когда EmailSender создается контейнером DI, контейнер внедряет экземпляр IOptions<EmailOptions> в конструктор EmailSender. EmailSender получает параметры сообщения с помощью свойства .Value и использует их в методе SendEmail для отправки письма.

Добавление SendGridClient в контейнер инъекции зависимостей

Вы сделали свое приложение более гибким и настраиваемым, но вы также можете еще больше интегрировать SendGrid в свое приложение. SendGrid SDK для .NET имеет дополнительный пакет NuGet, который добавит клиент SendGrid в контейнер внедрения зависимостей.

Выполните следующую команду, чтобы добавить пакет SendGrid DI:

dotnet add package SendGrid.Extensions.DependencyInjection
Войти в полноэкранный режим Выйти из полноэкранного режима

Затем обновите файл Program.cs следующим образом:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using SendGrid;
using SendGrid.Extensions.DependencyInjection;
using SendGrid.Helpers.Mail;

using IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices((context, services) =>
    {
        services.AddSendGrid(options =>
            options.ApiKey = context.Configuration["SendGrid:ApiKey"]
        );
        services.Configure<EmailOptions>(context.Configuration.GetSection("Email"));
        services.AddTransient<EmailSender>();
    })
    .Build();

var emailSender = host.Services.GetRequiredService<EmailSender>();
await emailSender.SendEmail();

public class EmailSender
{
    private readonly ISendGridClient sendGridClient;
    private readonly EmailOptions emailOptions;

    public EmailSender(IOptions<EmailOptions> emailOptions, ISendGridClient sendGridClient)
    {
        this.sendGridClient = sendGridClient;
        this.emailOptions = emailOptions.Value;
    }

    public async Task SendEmail()
    {
        var message = new SendGridMessage
        {
            From = emailOptions.From,
            Subject = emailOptions.Subject,
            PlainTextContent = emailOptions.Body
        };
        message.AddTo(emailOptions.To);
        var response = await sendGridClient.SendEmailAsync(message);

        // A success status code means SendGrid received the email request and will process it.
        // Errors can still occur when SendGrid tries to send the email. 
        // If email is not received, use this URL to debug: https://app.sendgrid.com/email_activity 
        Console.WriteLine(response.IsSuccessStatusCode ? "Email queued successfully!" : "Something went wrong!");
    }
}

public class EmailOptions
{
    public EmailAddress From { get; set; }
    public EmailAddress To { get; set; }
    public string Subject { get; set; }
    public string Body { get; set; }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Приложение добавляет клиент SendGrid в контейнер DI с помощью метода services.AddSendGrid.

Для настройки параметров SendGrid используется другая лямбда, которая передается в AddSendGrid. Лямбда-расширение может быть хитрым, поэтому убедитесь, что все ваши скобки соответствуют приведенному выше коду.

Вместо того чтобы создавать SendGridClient, как вы делали раньше, ISendGridClient теперь будет внедрен в конструктор класса EmailSender, а затем использован для отправки письма, как и раньше.

Почему так сложно?

Вы перешли от примерно 12 простых строк кода к примерно 26 строкам кода с классами, интерфейсами, лямбда-выражениями, родовыми типами и многим другим.

Зачем так усложнять код?

Для небольшого примера, где вы хотите как можно быстрее добраться из точки A в точку B, вы можете использовать переменные среды и на этом закончить, но для реальных приложений, хотя это и требует дополнительной настройки, приемы из этого руководства уменьшат количество кода, который вам нужно написать, и увеличат гибкость и сопровождаемость вашего решения.

Улучшенная конфигурация .NET

Пройдя этот учебник, вы научитесь использовать API конфигурации .NET для:

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

Поздравляем вас с тем, что вы добрались до конца длинного поста о конфигурации! 👏

Вы можете начать использовать эти техники для SendGrid, Twilio или любого другого приложения .NET! Если у вас возникнут какие-либо проблемы, вы можете обратиться к исходному коду этого руководства на GitHub или отправить проблему в репозиторий GitHub. В дополнение к конечному результату, вы можете найти каждый шаг в отдельной ветке (Шаг 1, 2, 3, 4, 5 и 6).

Дайте мне знать, над чем вы работаете. Мне не терпится увидеть, что вы создадите!

Оцените статью
Procodings.ru
Добавить комментарий