ASP .NET Core MVC - Criando um formulário de contato com MailKit


Hoje veremos como criar um formulário de contato usando a biblioteca MailKit que é open-source.

Se você já enviou emails em projetos ASP .NET Core usando a classe SMTPClient da plataforma .NET conhece as suas limitações e os seus percalços.


Pois não faz muito (a alguns anos) que esta classe foi marcada como obsoleta e a documentação oficial não a recomenda apontando em seu lugar a utilização da biblioteca MailKit que é  de código aberto
, extensível e é construída no .NET Standard, o que significa que você pode usar o mesmo código em projetos .NET Full Framework, UWP e .NET Core.
 

Neste artigo eu vou mostrar como criar um formulário de contato usando a biblioteca MailKit. Para fazer isso vamos criar um serviço como uma abstração em torno desta biblioteca pois se ela for posteriormente substituída ou tiver uma alteração profunda não teremos muito trabalho para fazer os ajustes. Agindo desta forma podemos mapear como queremos que nosso serviço seja antes de nos preocuparmos com os detalhes da implementação. Podemos ter uma visão de alto nível do envio de um e-mail, por exemplo, sem ter que nos preocupar exatamente como o MailKit funciona.

 

Como exemplo vou criar um formulário de contato e enviar emails usando o MailKit em um projeto ASP .NET Core MVC no ambiente do .NET 6.
 

A título de esclarecimento vou descrever os serviços STMP e POP que iremos usar neste artigo:

 

1- POP3
 

O Post Office Protocol versão 3 (POP3) é um protocolo de correio padrão usado para receber e-mails de um servidor remoto para um cliente de e-mail local. Ele permite que você baixe mensagens de e-mail em seu computador local e as leia mesmo quando estiver off-line.


Observe que, quando você usa o POP3 para se conectar à sua conta de e-mail, as mensagens são baixadas localmente e removidas do servidor de e-mail. Isso significa que, se você acessar sua conta de vários locais, essa pode não ser a melhor opção para você. Por outro lado, se você usa POP3, suas mensagens são armazenadas em seu computador local, o que reduz o espaço que sua conta de e-mail usa em seu servidor web.
 

Por padrão, o protocolo POP3 funciona em duas portas:

2- SMTP

 

O Simple Mail Transfer Protocol (SMTP) é o protocolo padrão para enviar e-mails pela Internet. É usado quando o e-mail é entregue a partir de um cliente de e-mail a um servidor de e-mail ou quando o e-mail é entregue a partir de um servidor de e-mail para outro.

Por padrão, o protocolo SMTP funciona em três portas:

Os recursos que iremos usar para criar este projeto são:

Criando o projeto ContatoMvc

Abra o Visual Studio 2022 e acione a opção Create new project selecionando o template : ASP.NET Core Web App (Model-View-Controller)

Informe o nome ContatoMvc e a seguir selecione a configuração conforme mostra a figura:

Clique em Create para criar a solução.

Vamos instalar o pacote MailKit usando o menu Tools do VS 2022 :

Você pode instalar usando o comando :  install-package MailKit

Além disso vamos incluir na pasta wwwroot/images as imagens que iremos usar no projeto.

Vamos ajustar a view Index.cshtml do controlador HomeController para exibir uma página inicial:

@{
    ViewData["Title"] = "Home Page";
}
<img src="~/images/emails.jpg" />
<div class="text-center">
    <h2 class="display-4">Emails</h2>
</div>

Preparando a infraestrutura

Vamos agora criar as classes para abstrair o serviço do MailKit.

Na pasta Models crie a classe EmailEndereco que representa o endereço de email:

namespace MvcContato.Models;

public class EmailEndereco
{
   public string? Nome { get; set; }
   public string? Endereco { get; set; }
}

A seguir vamos criar a classe EmailMensagem que descreve uma mensagem de email contendo os elementos mais comuns envolvidos em uma mensagem de email:

namespace MvcContato.Models;

public class EmailMensagem
{
    public List<EmailEndereco> ParaEnderecos { get; set; } = new List<EmailEndereco>();
    public List<EmailEndereco> DeEnderecos { get; set; } = new List<EmailEndereco>();
    public string? Assunto { get; set; }
    public string? Conteudo { get; set; }
}

A seguir vamos definir a configuração do serviço de email definindo o servidor SMTP, portas, credenciais, etc. Para isso vamos criar uma pasta Services no projeto e nesta pasta vamos criar a interface IEmailServerConfiguracao :

namespace MvcContato.Services;

public interface IEmailServerConfiguracao
{
    string? SmtpServer { get; }
    int SmtpPort { get; }
    string? SmtpUsername { get; set; }
    string? SmtpPassword { get; set; }

    string? PopServer { get; }
    int PopPort { get; }
    string? PopUsername { get; }
    string? PopPassword { get; }
}

Para implementar esta interface vamos criar a classe EmailServerConfiguracao na pasta Services:

namespace MvcContato.Services;
public class EmailServerConfiguracao : IEmailServerConfiguracao
{
    public EmailServerConfiguracao(int _smtpPort = 587)
    {
        SmtpPort = _smtpPort;
    }
    public string? SmtpServer { get; set; }
    public int SmtpPort { get; }
    public string? SmtpUsername { get; set; }
    public string? SmtpPassword { get; set; }
    public string? PopServer { get; set; }
    public int PopPort { get; set; }
    public string? PopUsername { get; set; }
    public string? PopPassword { get; set; }
}

Precisamos definir as configurações no arquivo appsettings.json :

{
  "EmailConfiguration": {
    "SmtpServer": "smtp.myserver.com",
    "SmtpPort": 465,
    "SmtpUsername": "smtpusername",
    "SmtpPassword": "smtppassword",

    "PopServer": "popserver",
    "PopPort": 995,
    "PopUsername": "popusername",
    "PopPassword": "poppassword"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Criando o serviço para enviar e receber email

Vamos agora criar a interface IEmailService na pasta Services do projeto onde vamos definir o contrato para enviar e receber email:

using MvcContato.Models;

namespace MvcContato.Services;

public interface IEmailService
{
    void EnviarEmail(EmailMensagem mensagem);
 
   List<EmailMensagem> ReceberEmail(int maximo = 10);
}

Para implementar estes contratos vamos criar a classe concreta EmailService :

using MailKit.Net.Pop3;
using MailKit.Net.Smtp;
using MimeKit;
using MimeKit.Text;
using MvcContato.Models;

namespace MvcContato.Services;

public class EmailService : IEmailService
{
    private readonly IEmailServerConfiguracao _config;
    public EmailService(IEmailServerConfiguracao config)
    {
        _config= config;
    }

    public List<EmailMensagem> ReceberEmail(int maximo = 10)
    {
        using (var emailClient = new Pop3Client())
        {
            emailClient.Connect(_config.PopServer, _config.PopPort, true);
            emailClient.AuthenticationMechanisms.Remove("XOAUTH2");
            emailClient.Authenticate(_config.PopUsername, _config.PopPassword);

            List<EmailMensagem> emails = new List<EmailMensagem>();
            for (int i = 0; i < emailClient.Count && i < maximo; i++)
            {
                var mensagem = emailClient.GetMessage(i);
                var emailMensagem = new EmailMensagem
                {
                    Conteudo = !string.IsNullOrEmpty(mensagem.HtmlBody)
                               ? mensagem.HtmlBody : mensagem.TextBody,

                    Assunto = mensagem.Subject
                };
                emailMensagem.ParaEnderecos.AddRange(mensagem.To.Select(x =>
                             (MailboxAddress)x).Select(x => new EmailEndereco
                             { Endereco = x.Address, Nome = x.Name }));

                emailMensagem.DeEnderecos.AddRange(mensagem.From.Select(x =>
                             (MailboxAddress)x).Select(x => new EmailEndereco
                             { Endereco = x.Address, Nome = x.Name }));

                emails.Add(emailMensagem);
            }

            return emails;
        }
    }

    public void EnviarEmail(EmailMensagem emailMensagem)
    {
        var mensagem = new MimeMessage();
        mensagem.To.AddRange(emailMensagem.ParaEnderecos.Select(x =>
                            new MailboxAddress(x.Nome, x.Endereco)));

        mensagem.From.AddRange(emailMensagem.DeEnderecos.Select(x =>
                              new MailboxAddress(x.Nome, x.Endereco)));

        mensagem.Subject = emailMensagem.Assunto;
        //Enviando HTML (podemos usar texto tambem)
        mensagem.Body = new TextPart(TextFormat.Html)
        {
            Text = emailMensagem.Conteudo
        };

        //Use a classe SmtpClient do Mailkit
        using (var emailClient = new SmtpClient())
        {
            //O ultimo parametro é para usar SSL
            emailClient.Connect(_config.SmtpServer, _config.SmtpPort, true);

            //Remover qualquer funcionalidade OAuth
            emailClient.AuthenticationMechanisms.Remove("XOAUTH2");
            emailClient.Authenticate(_config.SmtpUsername, _config.SmtpPassword);
            emailClient.Send(mensagem);
            emailClient.Disconnect(true);
        }
    }
}

Agora temos que incluir o serviço no contêiner DI da ASP .NET Core definindo o código abaixo no arquivo Program:

using MvcContato.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

var emailConfig = builder.Configuration.GetSection("EmailConfiguration")
                                       .Get<EmailServerConfiguracao>();

builder.Services.AddSingleton<IEmailServerConfiguracao>(emailConfig);
builder.Services.AddTransient<EmailEndereco>();
builder.Services.AddTransient<IEmailService, EmailService>();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

Agora podemos implementar o uso da implementação do nosso serviço usando o MailKit.

Criando o controlador

Vamos criar na pasta Controllers o controlador ContatoController onde vamos criar o método Action Index Get para apresentar o formulário de contato e o Post para processar o formulário e enviar o email.

using Microsoft.AspNetCore.Mvc;
using MvcContato.Models;
using MvcContato.Services;

namespace MvcContato.Controllers;

public class ContatoController : Controller
{
    private EmailEndereco enderecosOrigemDestino;
    private IEmailService EmailService;

    public ContatoController(EmailEndereco enderecosOrigemDestino,
        IEmailService emailService)
    {
        this.enderecosOrigemDestino = enderecosOrigemDestino;
        EmailService = emailService;
    }

    [HttpGet]
    public IActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public IActionResult Index(ContatoFormModel model)

    {
        if (ModelState.IsValid)
        {
            EmailMensagem msgEnviar = new EmailMensagem
            {
                DeEnderecos = new List<EmailEndereco> { enderecosOrigemDestino },
                ParaEnderecos = new List<EmailEndereco> { enderecosOrigemDestino },

                Conteudo = $"Sua mensagem: Nome: {model.Nome}, " +
                           $"Email: {model.Email}, Mensagem: {model.Mensagem}",

                Assunto = "Formulário de contato"
            };

            EmailService.EnviarEmail(msgEnviar);
            return RedirectToAction("Obrigado");
        }
        else
        {
            return Index();
        }
    }

    public IActionResult Obrigado()
    {
        return View();
    }
}

A biblioteca MailKit permite também uma implementação assíncrona, para isso basta usar os métodos SendAsync, AuthenticateAsync, DisconnectAsync e GetMessageAsync.

A seguir vamos criar a view Index.cshtml :

@model MvcContato.Models.ContatoFormModel

<img src="~/images/envemail.jpg" />

<h3>Formulário de Contato</h3>
<hr />
<div class="row">
    <div class="col-lg-12">
    <form asp-action="Index" asp-controller="Contato" method="post">
        <div>
            <input type="text" asp-for="Nome" class="form-control" placeholder="Seu nome*" required> 
        </div>
        <div>
            <input type="text" asp-for="Email" class="form-control" placeholder="Seu email*" required> 
        </div>
        <div>
            <div class="form-group"> 
              <textarea class="form-control" asp-form="Mensagem" placeholder="Sua mensagem*" required></textarea> 
              <p class="help-block text-danger"></p> 
           </div>
        </div>
        <input type="submit" value="Enviar Mensagem!" />
    </form>
    </div>
</div> 

E a view Obrigado.cshml :

<div class="jumbotron jumbotron-fluid">
  <div class="container">
    <h3 class="display-4">Grato pelo seu contato</h3>
      <p>Vou responder sua mensagem o mais brever possível.</p>  
      <hr class="my-4">
      <p class="lead">
       <div><a asp-action="Index" asp-controller="Home">Retornar</a></div>
     </p>
  </div>
</div>

Executando o projeto iremos obter o seguinte resultado:

Pegue o código do projeto aqui:  MvcContato.zip

"A minha porção é o Senhor, diz a minha alma; portanto esperarei nele."
Lamentações 3:24

Referências:


José Carlos Macoratti