Wprowadzenie
Tworząc systemy, staram się jak najwięcej korzystać z gotowych rozwiązań. Szczególnie w przypadku elementów, które są poboczne w rozwiązaniu, które tworzę, a jednak są wymagane do poprawnego działania aplikacji. Przykładem takiego elementu systemu jest wysyłka wiadomości email. Prawie każda aplikacja wysyła wiadomości email, a twórcy takich systemów jak SendGrid za drobną opłatą zrobią to dużo lepiej niż my.
SendGrid
SendGrid jest popularną usługą, która służy do wysyłki wiadomości email. Dosłownie w kilka minut możemy dodać do systemu wysyłkę wiadomości bez mocnego wgłębiania się w tematykę dostarczalności. Tym zajmują się twórcy usługi, my musimy tylko odpowiednio ją skonfigurować zgodnie z opisem z dokumentacji. Szczególnie ciekawą opcją są dynamiczne szablony (Dynamic Template), o których przeczytasz w dalszej części wpisu.
SendGrid jest rozbudowaną usługą, która ma sporo fajnych i przydatnych funkcjonalności. Na przykład dostaję informacje o aktywności wiadomości (dostarczenie wiadomości, otwarcie jej, kliknięcie w link). Do tego możemy te informacje przekazywać do naszej aplikacji i w jakiś sposób te dane wykorzystywać. Pokażę to w kolejnym wpisie.
Jeśli chodzi o koszt usługi, to mamy tutaj dwie opcje. Możemy skorzystać z usługi w cenie zgodnej ze standardowym cennikiem (https://sendgrid.com/pricing/), a wtedy na przykład za 50k wiadomości miesięcznie zapłacimy $14,95. Drugą opcją jest skorzystanie z oferty w ramach Azure. Ceny są troszkę niższe niż te w oficjalnym cenniku, a bardzo fajną opcją jest darmowy pakiet, w ramach którego możemy w miesiącu wysłać do 25k wiadomości (w normalnym cenniku tylko 100) – https://azuremarketplace.microsoft.com/en-us/marketplace/apps/sendgrid.tsg-saas-offer?tab=PlansAndPrice. Niestety w między czasie SendGrid zmienił warunki i już nie oferuje pakietu 25k wiadomości za darmo 🙁
Praktycznie wszystkie aplikacje, które rozwijam, są hostowane w Azure i zawsze korzystam domyślnie z tego darmowego pakietu, który w większości przypadków spokojnie wystarcza.
SendGrid a .NET
Na początku potrzebujemy utworzyć konto w SendGridzie, dodać informacje o wysyłającym i wygenerować klucz API, który posłuży nam do wysyłki wiadomości email. Kroki te są dość intuicyjne, więc nie będę ich tutaj szczegółowo pokazywał.
Mając przygotowane konto w SendGridzie, możemy przejść już do implementacji wysyłki. W pierwszej kolejności musimy gdzieś zapisać wygenerowany klucz API. W tym celu w przykładzie wykorzystałem dedykowaną sekcję w appsettings.json, ale równie dobrze może to być jakieś inne miejsce jak na przykład Azure Key Vault.
"SendGrid": { | |
"ApiKey": "wygenerowany klucz api", | |
"SenderEmail": "blogtest@plawgo.pl", | |
"SenderName": "Blog PROGRAMUJE.NET" | |
} |
Sekcje zmapowałem na klasę SendGridConfig:
public class SendGridConfig | |
{ | |
public string ApiKey { get; set; } | |
public string SenderEmail { get; set; } | |
public string SenderName { get; set; } | |
} |
Właściwe mapowanie jest robione w metodzie ConfigureServices w klasie Startup (linijka 3).
public void ConfigureServices(IServiceCollection services) | |
{ | |
services.Configure<SendGridConfig>(Configuration.GetSection("SendGrid")); | |
services.AddControllers(); | |
services.AddSwaggerGen(c => | |
{ | |
c.SwaggerDoc("v1", new OpenApiInfo { Title = "SendGridTests", Version = "v1" }); | |
}); | |
} |
Z SendGrida możemy korzystać na dwa sposoby: albo bezpośrednio z poziomu Web API, które udostępniają, albo możemy skorzystać z przygotowanego pakietu w Nugecie (https://www.nuget.org/packages/SendGrid/), który to opakowuje. Druga opcja jest łatwiejsza i ją wykorzystam we wpisie.
W przykładzie właściwy kod dodałem bezpośrednio do kontrolera, natomiast w normalnej sytuacji byłby on opakowany w jakąś usługę. Poniższy kod wysyła prostą wiadomość email:
[Route("api/[controller]")] | |
[ApiController] | |
public class SendGridController : ControllerBase | |
{ | |
private readonly SendGridConfig _config; | |
public SendGridController(IOptions<SendGridConfig> config) | |
{ | |
_config = config.Value; | |
} | |
[HttpPost] | |
public async Task<IActionResult> Send(SendDto request) | |
{ | |
var client = new SendGridClient(_config.ApiKey); | |
var msg = MailHelper.CreateSingleEmail(new EmailAddress(_config.SenderEmail, _config.SenderName), | |
new EmailAddress(request.Email, request.Name), | |
"Email Subject", | |
"Email body", | |
"<strong>Email body</strong>"); | |
var response = await client.SendEmailAsync(msg); | |
return await ProcessResponse(response); | |
} | |
private async Task<IActionResult> ProcessResponse(Response response) | |
{ | |
var responseContent = await response.Body.ReadAsStringAsync(); | |
return string.IsNullOrEmpty(responseContent) ? Ok() : BadRequest(responseContent); | |
} | |
} |
Do konstruktora kontrolera w pierwszej kolejności wstrzykujemy wcześniej skonfigurowaną klasę SendGridConfig.
W linijce 14 tworzona jest instancja klasy SendGridClient, która jest odpowiedzialna za wysyłkę wiadomości i do niej przekazujemy klucz API z konfiguracji. Kolejny krok to tworzenie wiadomości email poprzez określenie wysyłającego (wartości z konfiguracji), adresata (tutaj wartości przekazywane do akcji), tytułu oraz treści (w czystym tekście oraz HTML-u).
W linijce 23 następuje właściwe wysłanie wiadomości email. A później jest prosta obsługa odpowiedzi z SendGrida – tutaj zwrócenie z akcji. W zależności od właściwej aplikacji należałoby to jakoś ładnie obsłużyć.
Przykład działania
W efekcie działania powyższego kodu na maila otrzymamy takiego oto maila:
SendGrid poza samą wysyłką wiadomości email, daje nam również sporo informacji na temat tego, co się stało z wiadomością. Możemy sprawdzić, czy oraz kiedy email został otwarty przez użytkownika, czy kliknął w jakiś link:
Co fajne, możemy przygotować endpoint po stronie naszej aplikacji i SendGrid może do nas wysyłać informacje o statusie wiadomości. Na podstawie tego możemy później wykonać jakieś akcje w systemie.
Jak to zrobić, pokażę w kolejnym wpisie.
Dynamic Templates
Poza samą wysyłką wiadomości email, SendGrid udostępnia nam jeszcze jedną ważną funkcjonalność – Dynamic Templates.
W powyższym przykładzie to my z poziomu aplikacji wysyłaliśmy treść wiadomości email. W takim przypadku to po naszej stronie musimy przygotować jakiś szablon wiadomości, który następnie uzupełnimy w momencie wysłania wiadomości.
Dzięki Dynamic Templates możemy tę część przerzucić na SendGrida i zaoszczędzić sporo czasu nie implementując tego po naszej stronie. SendGrid udostępnia wizualny edytor, w którym możemy przygotować treść oraz wygląd wiadomości. Do takiej wiadomości możemy przekazać dane w postaci jsona, które następnie możemy wykorzystać w treści szablonu korzystając ze składni {{ nazwa pola z jsona }}. Co widać poniżej na zrzucie (szablon utworzyłem na podstawie gotowego szablonu z biblioteki):
Na zrzucie powyżej zaznaczyłem użycie pola Name właśnie w treści szablonu. Z poziomu edytora możemy ustawić zawartość jsona i testować sobie wygląd wiadomości bez konieczności jej wysyłki. Poniżej zrzut ekranu z ustawionym jsonem i uzupełnioną wartością w szablonie:
W SendGridzie możemy tworzyć sobie wiele różnych szablonów. Do tego mamy też wsparcie dla wersjonowania. Możemy tworzyć nowe wersje, przełączać się między nimi. Wszystko jest gotowe i nie musimy poświęcać na to czasu.
Do wysyłki wiadomości z użyciem szablonu będziemy potrzebowali jego id:
Wysyłka wiadomości z użyciem szablonu
Mając id szablonu, musimy dodać go do konfiguracji aplikacji. W przykładzie dodałem dla niego nowe pole w sekcji SendGrid w appsettings.json (linijka 5):
"SendGrid": { | |
"ApiKey": "wygenerowany klucz api", | |
"SenderEmail": "blogtest@plawgo.pl", | |
"SenderName": "Blog PROGRAMUJE.NET", | |
"TemplateId": "id szablonu" | |
} |
A później również i w SendGridConfig (linijka 9):
public class SendGridConfig | |
{ | |
public string ApiKey { get; set; } | |
public string SenderEmail { get; set; } | |
public string SenderName { get; set; } | |
public string TemplateId { get; set; } | |
} |
Natomiast sama wysyłka wiadomości jest bardzo podobna do tego, co było wcześniej:
[HttpPut] | |
public async Task<IActionResult> SendWithTemplate(SendDto request) | |
{ | |
var client = new SendGridClient(_config.ApiKey); | |
var msg = MailHelper.CreateSingleTemplateEmail(new EmailAddress(_config.SenderEmail, _config.SenderName), | |
new EmailAddress(request.Email, request.Name), | |
_config.TemplateId, | |
new { Name = request.Name}); | |
var response = await client.SendEmailAsync(msg); | |
return await ProcessResponse(response); | |
} |
Tym razem korzystamy z metody CreateSingleTemplateEmail, która poza wysyłającym oraz adresatem przyjmuje dwa parametry: id szablonu oraz obiekt z danymi. Id szablonu odczytujemy z ustawień, a obiekt z danymi tworzymy i przekazujemy. Na jego podstawie zostanie wygenerowany json i wysłany do SendGrida. Obsługa odpowiedzi jest taka sama jak wcześniej.
W efekcie wykonania powyższego kodu na skrzynką pocztową otrzymamy coś takiego:
Jak widać szablon został ładnie uzupełniony danymi, które do niego wysłaliśmy (tutaj Name miało wartość „Daniel Plawgo”).
Przykład
Na githubie (https://github.com/danielplawgo/SendGridTests) znajduje się przykład do tego wpisu. Do jego działania potrzebne jest przede wszystkim konto w SendGridzie. Po jego założeniu należy skonfigurować wysyłającego, wygenerować klucz API oraz utworzyć szablon. Następnie w appsettings.json ustawić wartości w sekcji SendGridConfig.
Kod wysyłający wiadomości znajduje się w kontrolerze SendGridController dostępnym pod adresem /api/SendGrid. Akcja typu POST wysyła wiadomość z treścią zdefiniowaną w kodzie. Natomiast akcja typu PUT korzysta z szablonu.
Podsumowanie
SendGrid jest bardzo ciekawą usługą, która potrafi zaoszczędzić bardzo dużo czasu, szczególnie gdy skorzystamy z szablonów wiadomości email. Dodatkowo oferta dostępna w ramach Azure powoduje, że przez dłuższy czas możemy cieszyć się z korzystania z darmowej wersji usługi.
W następnym wpisie pokażę, jak otrzymywać informacje z SendGrida o aktywnościach wysłanej wiadomości.
Podczas wysyłania maila dostajemy tylko zwrotkę, że sendgrid przyjął email do wysłania. Czy w jakiś sposób da się zweryfikować czy mail został wysłany np. pobierać identyfikator maila i odpytywać usługę(oczywiście w kodzie, a nie na portalu)?
Adam jest taka możliwość, SendGrid udostępnia funkcjonalność webhooków, gdzie możesz przygotować endpoint, który zostanie wywołany przez SendGrida, gdy coś wydarzy się z wysłaną wiadomością (np. jej dostarczenie, czy otworzenie przez użytkownika). Pisałem o tym w innym artykule: https://plawgo.pl/2021/02/23/sendgrid-webhook/
Dzięki za informację, na pewno się to przyda do roznego typu logów itp.