Wprowadzenie

Blog, którego prowadzę od paru ładnych lat, oparty jest o technologię ASP.NET MVC. Kiedy go zakładałem miałem tak naprawdę dwa wyjścia – Wordpress lub stack technologiczny, którego używałem w pracy. Zdecydowałem się na to drugie rozwiązanie ponieważ chciałem zobaczyć i przygotować w 100% projekt zaczynając od zera.

Wiecie jak wygląda praca na etacie nad danym projektem. Programiści są oddelegowani do implementacji różnych funkcjonalności i nie sposób jest dotknąć każdego zagadnienia. W tym jednak wypadku wszystko spoczywało na moich barkach - uważam to za niezwykle cenną lekcję.

W artykule poruszymy sposób tworzenia Newsletter’a na bazie mojego obecnego bloga.

Baza danych

Dostawcą usług hostingowych dla mojego bloga jest Webio. Był to mój pierwszy wybór, korzystam od kilku lat. Obecnie moje 3 projekty są opublikowane na ich serwerach. Na początku korzystałem z planu startowego, teraz z uwagi na większą liczbę domen czy baz danych przesiadłem się na jeden z wyższych planów. Hosting ten jest dla mnie intuicyjny w obsłudze (po zapoznaniu się z kluczowymi możliwościami) - nigdy również nie miałem problemów z obsługą. Jeżeli chcecie zapoznać się z zakresem ich usług zapraszam do skorzystania z linka affiliacyjnego https://www.webio.pl

W tym wpisie nie będziemy zajmować się tworzeniem systemu zarządzania wiadomościami e-mail (mam na myśli regularne powiadomienia o nowych artykułach na blogu). Naszym celem jest dodanie do bloga możliwości zapisania się na Newsletter, weryfikacji adresu odbiory oraz wyświetlenia wiadomości powitalnej.

Teraz już wiecie dlaczego zacząłem zagadnienie od bazy danych. Musimy utworzyć tabelę w której będziemy przechowywać najistotniejsze informacje. Mam tutaj na myśli adres e-mail Czytelnika, klucz weryfikacyjny oraz informację o potwierdzeniu tego adresu. Jeżeli adres nie został potwierdzony nie widzę żadnego sensu wysyłania powiadomień. Co więcej... ktoś mógł podać adres nie należący do danego Czytelnika.

Na potrzeby wpisu utworzyłem prostą tablę: Newsletter: tabela bazy danych

Implementacja

Czego potrzebujemy po stronie naszej aplikacji? Musimy dokonać połączenia z bazą danych, przygotować widok pozwalający na zapisanie się na newsletter, obsłużyć dane po stronie serwera oraz wysłać stosowne wiadomości e-mail.

Zaczniemy od front-endu. Wydaje mi się, że najlepszym miejscem będzie stopka naszej witryny. Najprostszy kod HTML przedstawia się następująco:

<div class="newsletter">
    <p>Newsletter</p>
    <p>
        Chcesz być na bieżąco z tym co dzieje się na blogu? Zapisz się poniżej:
    </p>
    <div>
        <form method="post" action="@Url.Action("Akcja", "Kontroler")">
            <input type="email" id="email" name="email" required />
            <input type="submit" value="Zapisz się" />
        </form>
    </div>
</div>
    
Na potrzeby bloga dodałem niezbędne style, żeby całość prezentowała się w przyjemny dla oka sposób: Newsletter: stopka witryny

Czytając ten wpis możecie zobaczyć, że w stopce dodałem zakładkę Polityka Prywatności. Wraz z utworzeniem newslettera na swoim blogu musimy powiadomić Czytelników kto jest administratorem danych osobowych, jakie prawa przysługują każdemu, etc. W takim wypadku lepiej być po bezpiecznej stronie...

Obsługa formularza

Jaki jest nasz następny krok? Przygotujemy kontroler, który podany adres e-mail doda do naszej bazy danych wraz z odpowiedniem kodem aktywacji. Kod ten zostanie wysłany na podany adres w celu poprawnej weryfikacji naszego Czytelnika. Zanim jednak do tego przejdziemy przygotujemy prosty szablon wysyłanej wiadomości tak, aby w kolejnym kroku być w pełnej gotowości na implementacje całej akcji.

O co chodzi z szablonami? Możemy przygotować plik HTML, który będzie wysyłany jako nasza wiadomość e-mail. Jest to niezwykle czytelny sposób formatowania wiadomości – nie robimy tego w kodzie. Jedyne na czym tak naprawdę nam zależy to wygenerowanie zwrotnego adresu url z zaszytym kodem. Adres ten wstrzykniemy w nasz szablon po czym wyślemy wiadomość.

W pierwszej kolejności dodamy pusty szablon do naszego projektu: Szablon wiadomości email

Na tym etapie przygotujemy prostą wiadomość weryfikacyjną – każdy z Was może ją dopasować do swoich potrzeb zmieniając logo, zmieniając kolorystkę, etc.:

<div class="confirmEmailTemplate" style="max-width:350px;background-color:rgb(240,240,240);margin-left:auto;margin-right:auto;margin-top:20px">
    <div class="logo" style="height:auto;border:1px solid rgb(240,240,240)">
        <img src="https://www.logaster.com/blog/wp-content/uploads/2018/05/LogoMakr.png" style="width:240px;height:auto;margin-left:auto;margin-right:auto;display:block;margin-top:15px;margin-bottom:15px" />
    </div>
    <div class="sep" style="height:2px;background-color:white"></div>
        <div class="form" style="margin-top:20px;padding-left:5px;padding-right:5px;padding-bottom:5px">
            <div class="form-group">
                <p>
                    <span style="font-weight:bold">
                        Witaj,
                    </span>
                    <br />
                    <span>
                        Dziękujemy za rejestrację w 'nazwa Twojej firmy'
                    </span>
                </p>
                <p>
                    Aby zakończyć konfigurację konta, potwierdź swój adres e-mail, klikając przycisk poniżej:
                </p>
                <p style="width:280px;margin-left:auto;margin-right:auto;text-align:center">
                    <a href="emailCallBackURL" style="width:280px;height:35px;margin-left:auto;margin-right:auto;display:block;padding-bottom:15px;color:white;background-color:rgb(115,221,1);">
                        <span style="padding-top:16px;display:block">Potwierdź swój adres e-mail</span>
                    </a>
                </p>
                <p>
                    <span style="font-weight:bold">
                        Dziękujemy,
                    </span>
                    <br />
                    <span>
                        Zespół 'nazwa Twojej firmy'
                    </span>
                </p>
            </div>
        </div>
    </div>
</div>

Tak przygotowany szablon możemy odpalić w przeglądarce i zobaczyć czy spełnia nasze oczekiwania: Szablon wiadomości email

Ja w międzyczasie dopasowałem layout swojej wiadomości – jesteśmy zatem gotowi na kolejne kroki.

Musimy zapisać dane formularza w bazie danych (wraz z wygenerowanym kodem), przygotować zwrotny link oraz wysłać samą wiadomość. Wszystkim zajmie się przygotowany przez nas kontroler. Spójrzcie na początek implementacji:

using System.Web.Mvc;
using MyPersonalWebsite.Models;

namespace MyPersonalWebsite.Controllers
{
    public class NewsletterController : Controller
    {
        // POST: Newsletter/Subscribe
        // Wysyłanie wiadomości e-mail
        [HttpPost]
        [AllowAnonymous]
        public async Task<ActionResult> Subscribe(string email)
        {
            try
            {
                return RedirectToAction("...");
            }
            catch
            {
                return View();
            }
        }

        // GET: Newsletter/ConfirmSubscription
        // Potwierdzenie zapisania się na newsletter
        [AllowAnonymous]
        [HttpGet]
        public ActionResult ConfirmSubscription(string email, Guid code)
        {
            try
            {
                return RedirectToAction("...");
            }
            catch
            {
                return View();
            }
        }

        // GET: Newsletter/Unsubscribe
        // Wypisanie się z listy
        [HttpGet]
        [AllowAnonymous]
        public ActionResult Unsubscribe(string email, Guid code)
        {
            try
            {
                return RedirectToAction("...");
            }
            catch
            {
                return View();
            }
        }

        // GET: Newsletter/Unsubscribe
        // A tutaj dodatkowa metoda pozwalająca zebrać feedback od Czytelników
        // Możesz poprosić o powód rezygnacji z subskrypcji newslettera
        [HttpPost]
        [AllowAnonymous]
        public ActionResult Unsubscribe(string message)
        {
            try
            {
                return RedirectToAction("...");
            }
            catch
            {
                return View();
            }
        }
    }
}

Jak widzicie przygotowałem prosty kontroler wraz z czterama możliwymi akcjami, tj. subskrypcja, potwierdzenie, wypisanie się z listy oraz prośba o powód wypisania się z newslettera. Dodatkowo, wykorzystując Entity Framework skonfigurowałem połączenie z bazą danych. Etap ten pomijam ponieważ pisałem artykuły dotyczące tego procesu zarówno dla ASP.NET MVC oraz ASP.NET Core.

Dodajmy zatem do tabeli bazy danych niezbędne dane:

using (PersonalWebsiteEntities db = new PersonalWebsiteEntities())
{
    Newsletter newsletterData = new Newsletter();
    newsletterData.Email = email;
    newsletterData.VerificationCode = verificationCode;
    newsletterData.IsVerified = false;

    db.Newsletter.Add(newsletterData);
    db.SaveChanges();
}

Połowa drogi za nami, musimy jeszcze wysłać e-mail do naszego Czytelnika. W tym celu wykorzystamy klasę IdentityConfig.cs gdzie dokonamy własnej implementacji i konfiguracji mechanizmu wysyłania wiadomości – stawiamy na czytelność i wielokrotne użycie kodu w różnych wariantach:

public Task SendAsync(string userEmail, string subject, string body)
{
    MailMessage mail = new MailMessage();
    mail.To.Add(userEmail);
    mail.From = new MailAddress("adres z którego wysyłamy wiadomość korzystając z usług dostawcy");
    mail.Subject = subject;

    // Formatowanie wiadomości
    StringBuilder sb = new StringBuilder();
    sb.AppendLine();
    sb.AppendLine(body);

    mail.Body = sb.ToString();
    mail.IsBodyHtml = true;

    SmtpClient smtp = new SmtpClient();
    smtp.Host = "smtp.webio.pl";
    smtp.Port = 587;
    smtp.UseDefaultCredentials = false;
    smtp.Credentials = new System.Net.NetworkCredential("dane_uzytkownika", "haslo");
    smtp.EnableSsl = true;
    smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
    return smtp.SendMailAsync(mail);
}
Pamiętajcie o odpowiedniej konfiguracji swojego konta pocztowego.

Baza danych przygotowana, szablon wiadomości sprawdzony, serwis pozwalający na wysłanie również – pora złożyć wszystko w logiczną całość:

// POST: Newsletter/Subscribe
// Wysyłanie wiadomości e-mail
[HttpPost]
[AllowAnonymous]
public async Task<ActionResult> Subscribe(string email)
{
    try
    {
        // kod weryfikacyjny
        Guid verificationCode = Guid.NewGuid();

        // dodajemy wymagane dane do odpowiedniej tabeli
        using (PersonalWebsiteEntities db = new PersonalWebsiteEntities())
        {
            Newsletter newsletterData = new Newsletter();
            newsletterData.Email = email;
            newsletterData.VerificationCode = verificationCode;
            newsletterData.IsVerified = false;

            db.Newsletter.Add(newsletterData);
            db.SaveChanges();
        }

        // Serwis pozwalający na wysyłanie wiadomości e-mail
        EmailService emailService = new EmailService();

        // Zwrotny adres url niezbędny do potwierdzenia subskrypcji
        var callbackUrl = Url.Action("ConfirmSubscription", "Newsletter", new { email = email, code = verificationCode }, protocol: Request.Url.Scheme);

        // Zwrotny adres url niezbędny do wypisania się z newslettera
        var emailCallBackUnsubscribeUrl = Url.Action("Unsubscribe", "Newsletter", new { email = email, code = verificationCode }, protocol: Request.Url.Scheme);

        // Wiadomość wygenerowana na bazie szablonu z załączonym adresem zwrotnym
        string htmlMessage = this.FormatVerificationMessage(callbackUrl, emailCallBackUnsubscribeUrl);

        await emailService.SendAsync(email, "Newsletter: potwierdź swój adres e-mail!", htmlMessage);

        // Widok potwierdzający wysłanie wiadomości i prośba o weryfikację
        return View();
    }
    catch (Exception ex)
    {
        // Widok informujący o błędzie...
        return RedirectToAction("Index", "Error", new { message = ex.Message, innerException = ex.InnerException.Message });
    }
}

#region Useful methods

/// <summary>
/// Formatowanie wiadomości na bazie dostarczonego szablonu oraz zwrotnego adresu url
/// </summary>
/// <param name="callBackUrl"></param>
/// <returns></returns>
public string FormatVerificationMessage(string callBackUrl, string emailCallBackUnsubscribeUrl)
{
    string htmlMessage = String.Empty;
    string filePath = String.Empty;

    filePath = HttpContext.Server.MapPath("~/Content/Templates/ConfirmEmailTemplate.html");
    htmlMessage = System.IO.File.ReadAllText(filePath);

    htmlMessage = htmlMessage.Replace("emailCallBackURL", callBackUrl);
    htmlMessage = htmlMessage.Replace("emailCallBackUnsubscribeUrl", emailCallBackUnsubscribeUrl);

    return htmlMessage;
}

#endregion

Jeżeli wszystko przebiegło pomyślnie wiadomość powinna zostać wysłana przez dostawcę Waszych usług. W ramach testów zapraszam Was do skorzystania z mojego newslettera:

Podsumowanie

Proces tworzenia własnej listy mailowej nie jest szczególnie trudny. Trzeba jednak uzbrobić się w cierpliwość ponieważ sama implementacja potrafi zająć sporo czasu - kiedy zaczniemy skupiać się na szczegółach zobaczymy jak czasochłonny jest to proces. W tym wpisie przeszliśmy przez wszystkie niezbędne punkty:

  • prosta tabela w bazie danych;
  • tworzenie szablonu wysyłanej wiadomości;
  • serwis pozwalający na konfigurację konta pocztowego i wysyłkę wiadomości.

Poruszliśmy również mechanizm weryfikacji (identyfikator zaszyty w adresie zwrotnym). W ramach domowej praktyki (jeżeli oczywiście tworzycie newsletter) dodajcie własną implementację dla metod ConfirmSubscription oraz Unsubscribe. Powyższy kod (z drobnymi modyfikacjami dla poszczególnych akcji) pozwoli na kompletne przygotowanie pierwszej listy mailowej.

Pamiętajcie również o uniemożliwieniu zapisania się dwukrotnie na ten sam adres e-mail oraz przygotowaniu odpowiednich widoków po stronie Waszej witryny internetowej.