Nastavení e-mailingu ve webové službě

06.04.2021

Posílání e-mailů je běžná operace, která je součástí mnoha aplikací. E-mail uživatele můžeme využít při registraci, při zapomenutí přihlašovacích údajů, ale také k informování o probíhajících nebo budoucích událostech. Dále můžeme e-mailem jednorázově sdílet soubory, například faktury, protokoly, potvrzení a podobně.

Slovníček

ASP.NET Core Open-source webový framework od Microsoftu (první release 2016).
MailKit Klientská knihovna pro protokoly SMTP/POP3/IMAP4.
System.Net.Mail.SmtpClient Knihovna od Microsoftu, která již není doporučovaná ve prospěch MailKitu.

Obsah

Implementace

V našem příkladě si připravíme webovou aplikaci, do které nainstalujeme balíček MailKit z NuGetu. Poté nastavíme konfiguraci a vytvoříme jednoduchou e‑mail službu, která bude přijímat údaje z konfigurace prostřednictvím konstruktoru. E‑mail služba bude využívat API MailKitu a umožní nám odesílat e-maily ve formátu HTML s možností přidat jednu nebo více příloh.

Příprava prostředí

Vytvoříme webový projekt ze základní šablony Visual Studia

dotnet new mvc --output Web

Instalace MailKitu a příprava konfigurace

Pomocí Package Manager Console ve Visual Studiu si nainstalujeme MailKit balíček

Install-Package MailKit

Do souboru appsettings.json přidáme novou sekci SmtpServer. Atributy si každý uživatel musí nastavit podle svého prostředí. V mém případě má cílový SMTP server adresu smtp.company.com a odesílací e-mailová adresa bude webapp@company.com.

Pokud SMTP server vyžaduje autentizaci, nedoporučuje se uvádět přihlašovací údaje přímo v konfiguračním souboru appsettings.json, protože by hrozilo, že se údaje dostanou do verzovacího systému, což je nežádoucí. Pro tento účel je lepší přihlašovací údaje nastavit buď jako globální proměnné (produkce, staging) nebo je přidat do souboru secrets.json (development).

appsettings.json
"SmtpServer": {
    "Address": "smtp.company.com",
    "Port": 25,
    "UseSsl": false,
    "From": "webapp@company.com"
  }

Pro účely autentizace v development prostředí nastavíme přihlašovací údaje v souboru secrets.json (Project > Manage User Secrets), který je uložený v %APPDATA%\microsoft\UserSecrets\<userSecretsId>\secrets.json a není tak součástí projektu.

secrets.json
{
  "SmtpServer": {
    "Username": "John.Doe@company.com",
    "Password": "P@55W02D"
  }
}

Připravíme si třídu, která bude obsahovat atributy ze SmtpServer sekce

namespace Web.Configuration
{
    public class SmtpServerConfiguration
    {
        public string Address { get; set; }
        public int Port { get; set; }
        public bool UseSsl { get; set; }
        public string From { get; set; }
        public string Username { get; set; }
        public string Password { get; set; }
    }
}

Třídu SmtpServerConfiguration si inicializujeme ve Startup.cs a rovnou zaregistrujeme i e-mail službu, kterou si následně vytvoříme. Na tomto místě může být služba zaregistrována také jako singleton.

var smtpServer = new SmtpServerConfiguration();
Configuration.Bind("SmtpServer", smtpServer);
services.AddTransient((System.Func<System.IServiceProvider, Infrastructure.Services.Email.IEmailService>)(service => 
    new EmailService(
        smtpServer.Address, 
        smtpServer.Port, 
        smtpServer.UseSsl, 
        smtpServer.From,
        //smtpServer.Username,
        //smtpServer.Password
        )));

E-mail služba

IEmailService.cs

using System.Collections.Generic;

namespace Web.Infrastructure.Services.Email
{
    public interface IEmailService
    {
        void Send(string to, string subject, string body, List<Attachment> attachments = null);
    }
}

Attachment.cs

namespace Web.Infrastructure.Services.Email
  {
      public class Attachment
      {
          public string FileName { get; set; }
          public byte[] Content { get; set; }
          public Attachment(string fileName, byte[] content)
          {
              FileName = fileName;
              Content = content;
          }
      }
  }

EmailService.cs

using MimeKit;
using System.Collections.Generic;

namespace Web.Infrastructure.Services.Email
{
    public class EmailService : IEmailService
    {
        private readonly string _smptServer;
        private readonly int _port;
        private readonly bool _useSsl;
        private readonly string _from;
        private readonly string _username;
        private readonly string _password;

        public EmailService(string smtpServer, int port, bool useSsl, string from, string username = null, string password = null)
        {
            _smptServer = smtpServer;
            _port = port;
            _useSsl = useSsl;
            _from = from;
            _username = username;
            _password = password;
        }

        public void Send(string to, string subject, string htmlBody, List<Attachment> attachments = null)
        {
            var message = new MimeMessage();

            BuildBody(htmlBody, attachments, message);

            Execute(to, subject, message);
        }

        private void BuildBody(string htmlBody, List<Attachment> attachments, MimeMessage message)
        {
            var bodyBuilder = new BodyBuilder { HtmlBody = new TextPart("html") { Text = htmlBody }.Text };

            if (attachments != null)
            {
                foreach (var attachment in attachments)
                {
                    bodyBuilder.Attachments.Add(fileName: attachment.FileName, data: attachment.Content);
                }
            }

            message.Body = bodyBuilder.ToMessageBody();
        }

        private void Execute(string to, string subject, MimeMessage message)
        {
            message.From.Add(MailboxAddress.Parse(_from));

            message.To.Add(MailboxAddress.Parse(to));

            message.Subject = subject;

            using var client = new MailKit.Net.Smtp.SmtpClient();

            client.Connect(_smptServer, _port, _useSsl);

            if (_username != null && _password != null)
            {
                client.Authenticate(_username, _password);
            }

            client.Send(message);

            client.Disconnect(true);
        }
    }
}

Závěr

Nyní máme k dispozici rozhraní (interface) i implementaci e-mail služby, kterou jsme zaregistrovali v souboru Startup.cs. Konfiguraci máme nastavenou v souboru appsettings.json. E-mail službu můžeme snadno volat napříč projektem, například v controlleru nebo jiné třídě, a pro účely testování ji můžeme nahradit za test double.

Odkazy