segunda-feira, 21 de agosto de 2017

Enviando e-mails com C#


Hoje em dia é muito comum termo que implementar o envio de e-mails através dos nossos aplicativos. Quando desenvolvemos uma aplicação utilizando o .NET Framework, essa necessidade é facilmente suprida, uma vez que o próprio .NET Framework contém classes que implementam o envio de e-mails. Confira neste artigo como enviar e-mails com C#.
As classes responsáveis pelo envio de e-mails no .NET Framework são a SmtpClient e MailMessage. Como o próprio nome já diz, a classe SmtpClient implementa as funcionalidades de SMTP (Simple Mail Transfer Protocol), que justamente é a responsável por fazer o envio do e-mail em si. Já a classe MailMessage representa uma mensagem de e-mail, com todas as propriedades que um e-mail contém, como, por exemplo, assunto, endereços do remetente e destinatário, etc.
Para aprendermos como funciona o envio de e-mails com essas classes, vamos criar um novo projeto do tipo Windows Forms. Nesse novo projeto, renomeie “Form1” para “FormEnviarEmail” e adicione alguns controles (basicamente Labels, TextBoxes, um Button e um RichTextBox – para digitarmos o conteúdo do e-mail), de forma que ele fique parecido com a imagem a seguir.
Feito isso, podemos implementar o código que fará o envio do e-mail no clique do botão “Enviar“.

Configurando o SmtpClient para funcionar com o Gmail

O primeiro passo que temos que seguir para enviarmos e-mails no C# é criarmos e configurarmos um SmtpClient. Esse SmtpClient terá todas as informações relacionadas ao servidor SMTP que fará o envio do e-mail. Você pode utilizar o servidor SMTP que quiser, mas, como é muito comum que qualquer pessoa tenha um e-mail do Gmail, vamos conferir neste artigo como configurar o SmtpClient para utilizar o SMTP do Gmail com as suas credenciais.
As propriedades que temos que configurar no SmtpClient são: HostPortEnableSslUseDefaultCredentials e Credentials. A propriedade “Host” representa o endereço do servidor SMTP e a propriedade “Port” representa a porta que o servidor SMTP utiliza (por padrão “25“). Já as outras propriedades (“EnableSsl“, “UseDefaultCredentials” e “Credentials“) servem para realizar a autenticação no servidor.
O endereço do servidor SMTP do Gmail é “smtp.gmail.com“, e a porta utilizada pelo Gmail não é a padrão (25), mas sim, a porta 587. A página de ajuda do Gmail apresenta todas essas informações para você:
Um erro que eu cometi durante os meus testes foi utilizar a porta 465 ao invés da porta 587 (talvez enganado pelas informações deste link). Como na documentação estava escrito “Porta para SSL“, achei que essa fosse a porta que eu deveria utilizar, mas, na realidade, a porta que precisamos utilizar no SmtpClient é a “Porta para TLS“.
Uma vez configuradas as propriedades “Host” e “Port“, temos que configurar a propriedade “EnableSsl” para “true“, a propriedade “UseDefaultCredentials” para “false” e a propriedade “Credentials” com as credenciais da sua conta do Gmail. Confira abaixo como fica o trecho de código com a configuração do SmtpClient:
1
2
3
4
5
6
7
8
using (System.Net.Mail.SmtpClient smtp = new System.Net.Mail.SmtpClient())
{
    smtp.Host = "smtp.gmail.com";
    smtp.Port = 587;
    smtp.EnableSsl = true;
    smtp.UseDefaultCredentials = false;
    smtp.Credentials = new System.Net.NetworkCredential("SEUEMAIL@gmail.com", "SUASENHA");
}
Atenção! É importante que a configuração da propriedade “UseDefaultCredentials” seja feita antes da configuração da propriedade “Credentials“. Se você inverter essas duas linhas, você receberá o seguinte erro:
The SMTP server requires a secure connection or the client was not authenticated. The server response was: 5.5.1 Authentication Required.
Isso acontece porque, ao configurarmos a propriedade “UseDefaultCredentials” para “false“, o .NET Framework automaticamente faz um “reset” da propriedade “Credentials“, ou seja, ela será alterada para “null“. Portanto, temos que configurar as “Credentials” depois de ter alterado a propriedade “UseDefaultCredentials“.
Encontrei essa informação em um post no StackOverflow:

Construindo o MailMessage

Agora que já configuramos o SmtpClient, temos que criar um MailMessage, configurar suas propriedades e passá-lo ao método Send do SmtpClient. As propriedades que vamos configurar são: FromToCCBccSubject e Body. Veja como fica o resultado no trecho de código abaixo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using (System.Net.Mail.MailMessage mail = new System.Net.Mail.MailMessage())
{
    mail.From = new System.Net.Mail.MailAddress("SEUEMAIL@gmail.com");
 
    if (!string.IsNullOrWhiteSpace(textBoxPara.Text))
    {
        mail.To.Add(new System.Net.Mail.MailAddress(textBoxPara.Text));
    }
    else
    {
        MessageBox.Show("Campo 'para' é obrigatório.", "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
    }
    if (!string.IsNullOrWhiteSpace(textBoxCC.Text))
        mail.CC.Add(new System.Net.Mail.MailAddress(textBoxCC.Text));
    if (!string.IsNullOrWhiteSpace(textBoxCCo.Text))
        mail.Bcc.Add(new System.Net.Mail.MailAddress(textBoxCCo.Text));
    mail.Subject = textBoxAssunto.Text;
    mail.Body = richTextBoxCorpo.Text;
}
Observe que a única particularidade do código acima é que os endereços de e-mail não são simplesmente strings, mas sim, instâncias de MailAddress.
O único detalhe que está faltando para que o e-mail seja enviado é a chamada do método “Send” do SmtpClient. Nele passaremos a MailMessage que acabamos de criar:
1
smtp.Send(mail);

Anexando arquivos

Para anexarmos arquivos à nossa mensagem, basta utilizarmos a propriedade Attachments da classe MailMessage. Porém, antes de conferirmos como fica o código, vamos alterar um pouco a nossa janela, adicionando uma ListBox e um botão para escolhermos os arquivos a serem anexados ao e-mail:
Confira abaixo o código a ser utilizado no evento “Click” do botão “Adicionar“:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void buttonAdicionarAttachment_Click(object sender, EventArgs e)
{
    using (OpenFileDialog dialog = new OpenFileDialog())
    {
        dialog.Multiselect = true;
 
        if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
        {
            foreach (var file in dialog.FileNames)
            {
                listBoxAttachments.Items.Add(file);
            }
        }
    }
}
Como você pode ver, é um código muito simples. Nós utilizamos a classe OpenFileDialog para selecionarmos um ou mais arquivos (note que a propriedade MultiSelect foi alterada para “true” a fim de possibilitarmos a escolha de mais de um arquivo) e, caso o usuário confirme o diálogo, nós preenchemos o ListBox com os caminhos para os arquivos escolhidos. Uma solução mais robusta seria possibilitar o drag and drop de arquivos diretamente do Windows Explorer para dentro desse ListBox. Você pode conferir como implementar essa funcionalidade no meu artigo sobre drag and drop em aplicações Windows Forms com C#.
Feito isso, adicione estas três linhas de código antes da chamada do método “Send” para que os arquivos sejam adicionados à mensagem de e-mail:
1
2
3
4
5
6
foreach (string file in listBoxAttachments.Items)
{
    mail.Attachments.Add(new System.Net.Mail.Attachment(file));
}
 
smtp.Send(mail);
E com isso podemos anexar arquivos ao nosso e-mail clicando no botão “Adicionar“.

Formatação HTML

Por padrão, a classe MailMessage utiliza o formato “texto” para as mensagens de e-mail. Porém, essa classe suporta também o envio de mensagens no formato HTML. Pensando nisso, eu resolvi substituir o controle RichTextBox por um controle que suportasse a formatação HTML.
Encontrei uma opção gratuita, chamada “ModelText HTML Edit Control“, porém, sua API é tão estranha e a documentação tão ruim que não consegui fazer com que esse controle funcionasse. Outra opção gratuita que encontrei foi um editor HTML postado no blog de um funcionário da Microsoft, mas, como achei ele extremamente complicado de utilizar, achei que nem valesse a pena o esforço. Encontrei também outras duas – opções que achei complicadas demais. Portanto, minha sugestão: se você precisar de um controle HTML na sua aplicação, separe bastante tempo para analisar essas opções gratuitas ou compre um controle comercial (como SpiceLogic .NET Win HTML Editor ControlXStandard Editor ou DevExpress RichEdit – esse último utilizamos na empresa onde eu trabalho).
A segunda ideia que tive para contornar esse problema foi: será que não tem como converter o RTF do RichTextBox em HTML? Após pesquisar, encontrei este exemplo de código que mostra como converter RTF para HTML. Resolvi dar uma chance e tentar utilizá-lo. Compilei a biblioteca, adicionei uma referência ao projeto e alterei a configuração da propriedade “Body” da MailMessage para fazer a conversão do conteúdo do RichTextBox de RTF em HTML:
1
mail.Body = MarkupConverter.RtfToHtmlConverter.ConvertRtfToHtml(richTextBoxCorpo.Rtf);
Porém, apesar do resultado ter sido animador, o e-mail ainda foi enviado em formato texto.
Isso aconteceu porque eu esqueci de alterar a propriedade IsBodyHtml para “true” antes de configurar o HTML no “Body” da MailMessage. Ao resolver esse detalhe, o resultado foi um pouco diferente (mas, nada animador):
1
2
mail.IsBodyHtml = true;
mail.Body = MarkupConverter.RtfToHtmlConverter.ConvertRtfToHtml(richTextBoxCorpo.Rtf);
Repare na barra de rolagem na imagem acima. Não sei porque, mas, o HTML convertido ficou com inúmeros espaços entre uma linha e outra.
Depois desse fracasso, resolvi desistir de enviar o e-mail no formato HTML com essas opções gratuitas. Se você tiver essa necessidade na sua aplicação, eu sugiro que você compre um controle que suporte edição HTML.

Envio assíncrono de e-mails

Ao utilizarmos o método “Send” da classe SmtpClient, o envio será feito de forma síncrona, ou seja, a interface ficará bloqueada enquanto o e-mail estiver sendo enviado. Porém, a classe SmtpClient suporta duas modalidades para envio de e-mail assíncrono: os métodos SendAsync ou SendMailAsync. A diferença entre esses dois métodos (como você pode conferir nesta discussão no StackOverflow) é que o método “SendAsync” utiliza o esquema de “call-backs“. Já o método “SendMailAsync” utiliza o esquema dos operadores async/await. Se você tiver a possibilidade de utilizar async e await na sua aplicação, eu recomendo que você considere a opção “SendMailAsync“, que é muito mais simples.
Confira como ficou o código final, ajustado para que o e-mail seja enviado de forma assíncrona utilizando o método “SendMailAsync“:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
private async void buttonEnviar_Click(object sender, EventArgs e)
{
    using (System.Net.Mail.SmtpClient smtp = new System.Net.Mail.SmtpClient())
    {
        smtp.Host = "smtp.gmail.com";
        smtp.Port = 587;
        smtp.EnableSsl = true;
        smtp.UseDefaultCredentials = false;
        smtp.Credentials = new System.Net.NetworkCredential("SEUEMAIL@gmail.com", "SUASENHA");
 
        using (System.Net.Mail.MailMessage mail = new System.Net.Mail.MailMessage())
        {
            mail.From = new System.Net.Mail.MailAddress("SEUEMAIL@gmail.com");
 
            if (!string.IsNullOrWhiteSpace(textBoxPara.Text))
            {
                mail.To.Add(new System.Net.Mail.MailAddress(textBoxPara.Text));
            }
            else
            {
                MessageBox.Show("Campo 'para' é obrigatório.", "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            if (!string.IsNullOrWhiteSpace(textBoxCC.Text))
                mail.CC.Add(new System.Net.Mail.MailAddress(textBoxCC.Text));
            if (!string.IsNullOrWhiteSpace(textBoxCCo.Text))
                mail.Bcc.Add(new System.Net.Mail.MailAddress(textBoxCCo.Text));
            mail.Subject = textBoxAssunto.Text;
            mail.Body = richTextBoxCorpo.Text;
 
            foreach (string file in listBoxAttachments.Items)
            {
                mail.Attachments.Add(new System.Net.Mail.Attachment(file));
            }
 
            await smtp.SendMailAsync(mail);
        }
    }
}
Note que as únicas diferenças são: alteramos a chamada de “Send” para “SendMailAsync“; adicionamos a palavra-chave “await” antes da chamada de “SendMailAsync“; adicionamos a palavra-chave “async” na assinatura do método.

Concluindo

Hoje em dia, envio de e-mails é uma funcionalidade muito comum nas aplicações de negócios. Para a nossa sorte, a Microsoft disponibiliza classes no .NET Framework que implementam essa funcionalidade. Nesse artigo você conferiu como enviar e-mails com as classes SmtpClient e MailMessage, incluindo a possibilidade de anexar arquivos nas mensagens e fazer o envio de forma assíncrona. Você acompanhou também a minha luta (perdida) ao tentar utilizar editores gratuitos que suportassem o formato HTML (ou até mesmo a conversão de RTF para HTML). Agora, não perca tempo e implemente essa funcionalidade nos seus aplicativos, caso seja pertinente!
E, para finalizar, caso você queira ficar por dentro das novidades do meu site, assine a minha newsletter. Com ela você recebe um e-mail por semana comentando o artigo publicado, além de ficar sabendo em primeira mão sobre o artigo da próxima semana e também receber dicas exclusivas que eu só compartilho por e-mail. Assine utilizando este link ou o formulário abaixo.
Até a próxima!
André Lima

Nenhum comentário:

Postar um comentário