SendGrid – Webhook

Wprowadzenie

W jednym z wcześniejszych wpisów pokazałem, w jaki sposób wysłać wiadomość email z wykorzystaniem SendGrida. Wspomniałem również, że umożliwia on otrzymywanie zdarzeń o zmianie statusu wysłanej wiadomości. Możemy otrzymać takie informacje jak dostarczenie wiadomości, otworzenie przez użytkownika, kliknięcie w link. W tym wpisie pokażę Ci, jak otrzymywać te zdarzenia w C#.

SendGrid Webhook

W panelu konfiguracyjnym SendGrida możemy skonfigurować webhooka, za pomocą którego SendGrid prześle nam informacje o zdarzeniach związanych z wysłaną wiadomością email. W niektórych aplikacjach takie informacje mogą być bardzo cenne i na ich podstawie możemy wykonywać różne czynności w systemie.

Z drugiej strony, jeśli nawet nie masz potrzeby, aby reagować na przykład na otwarcie wiadomości email, to i tak warto zainteresować się tymi zdarzeniami. Podczas wysyłania wiadomości email należy dbać o jakość listy adresowej. Z czasem mimo wszystko może się okazać, że jakiś adres email już nie istnieje (np. ktoś już nie pracuje w firmie). Wysyłanie wiadomości do nieaktywnych adresów może wpłynąć na późniejszą dostarczalność.

Dzięki zdarzeniom z SendGrida możesz oznaczyć sobie taki problematyczny adres email i zaprzestać wysyłania wiadomości do niego. Dzięki temu nie spadnie Ci dostarczalność wiadomości, a również zaoszczędzisz na liczbie wiadomości w pakiecie.

A jak za chwilę zobaczysz, dodanie do aplikacji otrzymywania zdarzeń z SendGrida nie jest skomplikowane.

Darmowy kurs Visual Studio

Pracując z setkami programistów, zauważyłem, że większość osób nie pracuje efektywnie w Visual Studio. W skrajnych przypadkach korzystali z kopiowania z wykorzystaniem menu Edit. Wiem, że to dziwne, ale naprawdę niektórzy tak pracują. Dlatego postanowiłem stworzyć kurs Visual Studio – aby pomóc koleżankom i kolegom w efektywniejszej pracy.

Przygotowałem 30 lekcji e-mail, w których pokażę Ci, w jaki sposób pracować efektywniej i szybciej w Visual Studio. Poznasz dodatki, bez których nie wyobrażam sobie pracy w tym IDE.

Po więcej informacji zapraszam na dedykowaną stronę kursu: Darmowy Kurs Visual Studio.

Quiz C#

Ostatnio przygotowałem również quiz C#, w którym możesz sprawdzić swoją wiedzę. Podejmiesz wyzwanie?

Modyfikacja wysyłania wiadomości

Rozszerzę przykład z pierwszego wpisu o SendGridzie. Dlatego jeśli go jeszcze nie czytałeś/czytałaś, to warto w tej chwili to nadrobić – jak wysłać wiadomość email z użyciem SendGrida.

Na początku musimy troszeczkę zmodyfikować sposób wysyłania wiadomości. Do tej pory z odpowiedzi zwróconej przez SendGrida po wysłaniu wiadomości, sprawdzaliśmy tylko, czy nie ma żadnych błędów. W odpowiedzi w nagłówku znajduje się jeszcze jedna ważna rzecz: jest to id wiadomości. To id będziemy później otrzymywali w każdym zdarzeniu wysłanym przez SendGrida i na podstawie tego będziemy mogli określić, jakiej wiadomości dotyczy.

Id znajduje się w nagłówku odpowiedzi o nazwie X-Message-Id. W przykładzie dodałem odczyt tego nagłówka i wyświetlenie go w logach (konsoli aplikacji):

private async Task<IActionResult> ProcessResponse(Response response)
{
var responseContent = await response.Body.ReadAsStringAsync();
if (string.IsNullOrEmpty(responseContent) == false)
{
return BadRequest(responseContent);
}
var header = response.Headers.FirstOrDefault(h => h.Key == "X-Message-Id");
var messageId = header.Value?.FirstOrDefault();
_logger.LogInformation($@"MessageId: {messageId}");
return Ok(new
{
MessageId = messageId
});
}

W normalnej aplikacji to id zapisalibyśmy gdzieś w bazie wraz z informacją, do kogo wysłaliśmy wiadomość email i dlaczego.

Uwierzytelnienie Webhooka

Zanim przejdziemy do dodawania akcji, do której SendGrid będzie przesyłał nam zdarzenia, parę słów na temat tego, w jaki sposób upewnimy się, że to SendGrid przesyła nam zdarzenia.

W momencie pisania tego wpisu dostępne są 3 sposoby na uwierzytelnienie. Możemy w ogóle tego nie robić, jednak nie jest to zalecane. Możemy zweryfikować podpis wysłanego zdarzenia za pomocą klucza publicznego. Natomiast trzecim sposobem jest skorzystanie z OAuth 2.0.

W przykładzie skorzystam z drugiego podejścia, czyli z weryfikacji podpisu zdarzenia. Gdy korzystasz z OAuth 2.0 w swojej aplikacji, możesz z niego skorzystać.

W panelu SendGrida (w dalszej części wpisu będzie informacja, gdzie dokładnie to zrobić) możemy wygenerować klucz publiczny oraz prywatny. Kluczem prywatnym SendGrid podpisze zdarzenie, natomiast my za pomocą klucza publicznego będziemy weryfikowali poprawność zdarzenia.

Dlatego potrzebujemy przechować klucz publiczny w aplikacji. W tym celu w pliku konfiguracyjnym appsettings.json do sekcji SendGrid dodaję nowe pole WebhooksKey:

"SendGrid": {
"ApiKey": "apikey",
"SenderEmail": "blogtest@plawgo.pl",
"SenderName": "Blog PROGRAMUJE.NET",
"TemplateId": "templateid",
"WebhooksKey": "publickey"
}

Do tego właściwość WebhooksKey w klasie SendGridConfig:

public class SendGridConfig
{
public string ApiKey { get; set; }
public string SenderEmail { get; set; }
public string SenderName { get; set; }
public string TemplateId { get; set; }
public string WebhooksKey { get; set; }
}

Dodanie akcji dla Webhooka

W tym momencie możemy przejść do definicji kontrolera i akcji, którą będzie wywoływał SendGrid w momencie wysłania zdarzenia. Niestety w oficjalnej bibliotece do SendGrida nie znajdziemy wsparcia dla obsługi Webhooków. Ale na szczęście na githubie i w nugecie jest dostępna alternatywna biblioteka, która nam to ułatwi – StrongGrid.

Po jej instalacji w projekcie dodajemy taki kontroler:

[Route("api/[controller]")]
[ApiController]
public class SendGridWebhooksController : ControllerBase
{
private readonly SendGridConfig _config;
private readonly ILogger<SendGridWebhooksController> _logger;
public SendGridWebhooksController(IOptions<SendGridConfig> config,
ILogger<SendGridWebhooksController> logger)
{
_logger = logger;
_config = config.Value;
}
[HttpPost]
[Route("InboundEmail")]
public async Task<IActionResult> ReceiveInboundEmail()
{
try
{
var signature = Request.Headers[WebhookParser.SIGNATURE_HEADER_NAME];
var timestamp = Request.Headers[WebhookParser.TIMESTAMP_HEADER_NAME];
var parser = new WebhookParser();
var events = await parser.ParseSignedEventsWebhookAsync(Request.Body, _config.WebhooksKey, signature, timestamp);
_logger.LogInformation(JsonConvert.SerializeObject(events));
return Ok();
}
catch (SecurityException e)
{
return BadRequest();
}
}
}

Do kontrolera wstrzykujemy dwie rzeczy: instancje klasy SendGridConfig, w której jest między innymi wcześniej dodany klucz publiczny oraz logger. W przykładzie na konsoli będziemy wyświetlali sparsowane zdarzenia.

Kontroler zawiera jedną akcję typu POST (linijka 16), która właśnie posłuży do obsługi zdarzeń. Na początku z nagłówków odczytujemy dwa specjalne nagłówki, które za chwilę wykorzystamy do weryfikacji podpisu zdarzeń (linijka 20-21).

Klasa WebhookParser służy do parsowania treścią żądania POST. W tym przypadku wykorzystałem metodę ParseSignedEventsWebhookAsync (linijka 24), która przy okazji weryfikuje jeszcze podpis wiadomości. Przekazujemy do niej treść żądania, publiczny klucz oraz wcześniej wyciągnięte nagłówki. Metoda natomiast zwraca listę zdarzeń (obiektów C#), które w sobie zawierają między innymi MessageId. To jest ta sama wartość, którą otrzymaliśmy w nagłówku po wysłaniu wiadomości do SendGrida.

Mając już sparsowane zdarzenia, możemy coś z nimi zrobić. W przykładzie wyświetlam je na konsoli, ale w realnej aplikacji tutaj wykonaliśmy jakąś logikę. SendGrid za jednym razem może nam wysłać większą ilość zdarzeń, dlatego metoda parsująca zwraca tablicę obiektów.

Z akcji zwracamy status 200, który informuje SendGrida, że poprawnie przetworzyliśmy otrzymane zdarzenia. Gdy zwrócimy inny status niż 2xx, to SendGrid przez 24 godzin będzie próbował wysłać jeszcze raz do nas informacje o zdarzeniach. Między innymi dlatego powinniśmy tak przygotować logikę aplikacji, aby poprawnie obsłużyła wielokrotne otrzymanie tego samego zdarzenia.

Konfiguracja Webhooków w SendGridzie

Mając już przygotowany kod, możemy przejść do konfiguracji SendGrida. W pierwszej kolejności potrzebujemy przygotować publiczny adres, pod którym będzie dostępna testowa aplikacji. Posłużę się w tym celu aplikacją ngrok, którą opisywałem w poprzednim wpisie i która umożliwia publiczne udostępnienie portu z lokalnego komputera. W moim przykładzie adresem będzie: [url z ngrok]/api/SendGridWebhooks/InboundEmail

Aby skonfigurować Webhooki w SendGrid należy zalogować się i w menu z lewej strony przejść do Settings => Mail Settings. W pierwszej sekcji mamy na samej górze dwie interesujące nas opcje: Event Webhook oraz Signed Event Webhook Requests:

Mail Settings w SendGrid

W opcji Event Webhook konfigurujemy naszego Webhooka. Określamy rodzaj autoryzacji (punkt 1). W tym przypadku zostawiamy None. Jak pisałem wcześniej, skorzystamy z podpisu wiadomości. Tutaj możemy ewentualnie skorzystać z OAuth2, gdy nasza aplikacja go wspiera.

Następnie określamy adres akcji, gdzie ma być wysłane żądanie POST (punkt 2). Jest to adres, o którym pisałem wyżej.

Za pomocą przycisku Test Your Integration (punkt 3) możemy przetestować działanie naszego adresu/akcji. Tutaj mam dwie uwagi. Po pierwsze sporadycznie ten przycisk nie do końca działa. Mimo poprawnej konfiguracji testowe zdarzenie może nie być wysyłane, a SendGrid będzie zgłaszał błąd. W takiej sytuacji najlepiej zalogować się ponownie i spróbować jeszcze raz. Po drugie ten przycisk nie będzie wysyłał podpisanego żądania, więc i tak nasza akcja w tym przypadku skończy się błędem.

Następnie określamy typy zdarzeń, jakie chcemy otrzymywać (punkt 4), włączamy Webhooki w punkcie 5 oraz wszystko zapisujemy przyciskiem Save (punkt 6):

Konfiguracja Webhook w SendGrid

W opcji Signed Event Webhook Requests włączamy podpisywanie zdarzeń (punkt 1), a następnie kopiujemy publiczny klucz (punkt 2) do pliku appsettings.json aplikacji:

SendGrid signed webhook

Test zdarzeń

Mając działającą aplikację oraz skonfigurowane Webhooki w SendGrid, możemy przejść do testów. Wysłałem jedną wiadomość email za pomocą endpointu tworzonego w poprzednim wpisie. Na konsoli pojawiły się następujące logi:

SendGrid Webhook wynik

Na początku po logach związanych z startem aplikacji, wyświetlone zostało MessageId. Jest to id, które zostało zwrócone z SendGrida po wysłaniu żądania do niego. Kolejne dwa wpisy dotyczą już zdarzeń. W treści otrzymaliśmy te same MessageId. Do tego status processed pierwszego zdarzenia oraz delivered drugiego.

SendGrid czasami przesyła kilka zdarzeń w jednym żądaniu. Tutaj natomiast były to dwa oddzielne żądania.

Przykład

Na potrzeby tego wpisu rozbudowałem przykład z pierwszego wpisu o SendGrid. Więc aby go uruchomić, musisz wykonać kroki opisane w tym wpisie. Następnie trzeba wygenerować publiczny klucz i ustawić go w pliku appsettings.json. Przykład znajduje się na githubie – https://github.com/danielplawgo/SendGridTests

Podsumowanie

Jak widać, obsługa zdarzeń z SendGrida nie jest jakoś mocno skomplikowana. Warto o niej pomyśleć, gdy korzystasz z tej usługi. Szczególnie, aby wyłapać adresy email, które z jakiegoś powodu już nie są poprawne. Co może przy dużej ich liczbie wpłynąć na dostarczalność Twoich wiadomości.

A Ty korzystasz z zdarzeń z SendGrida?

.NET 5 Web App w Azure

Szkolenie .NET 5 Web App w Azure

Zainteresował Ciebie ten temat? A może chcesz więcej? Jak tak to zapraszam na moje autorskie szkolenie o Web API działającym w Azure.

2 thoughts on “SendGrid – Webhook

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.