Dev Life
Enviando e-mail usando C# e a API do Mailgun
O .NET é um dos frameworks web mais populares, estáveis e admirados que você pode usar para criar aplicações web modernas. As ferramentas fornecidas pelo .NET podem ajudar você a começar rapidamente e a manter as suas aplicações por muito tempo no futuro. Com ele, você pode criar de tudo, desde pequenos microsserviços muito específicos até aplicações web completas.
Porém, em algum momento, toda aplicação real precisa ser capaz de enviar e-mails transacionais (como verificação de conta, redefinições de senha, relatórios de resumo diários/mensais automatizados e assim por diante). Embora o .NET ofereça ferramentas que ajudam você a enviar e-mails, ele não tem a infraestrutura com capacidade de produção necessária para enviá-los de forma confiável e eficiente.
Neste artigo, você aprenderá a usar a API HTTP do Mailgun para enviar e-mails transacionais de suas aplicações .NET e garantir que a sua integração seja confiável, escalável e segura.
Como enviar e-mail usando a API do Mailgun
Antes de começar este tutorial, você precisará dos seguintes pré-requisitos:
- Conhecimento básico de C# e .NET
- Qualquer IDE moderna, como Visual Studio, JetBrains Rider ou Visual Studio Code
Se você escolher não usar o Visual Studio e a execução de dotnet –info a partir de um terminal não for bem-sucedida, você precisará baixar e instalar o SDK do .NET mais recente.
Configure a sua conta do Mailgun
Comece criando uma conta gratuita do Mailgun. Depois de fazer o login, crie uma nova chave de API e armazene-a para uso posterior.
Para o envio de e-mail em produção, você precisa registrar um domínio que você possui no Mailgun. Neste tutorial, você usará o domínio sandbox fornecido pelo Mailgun.
Crie um novo projeto em C#
Depois que a sua conta do Mailgun estiver configurada, você precisa criar uma nova solução web .NET MVC e configurar algumas ferramentas adicionais para ajudar a enviar e-mails transacionais com o .NET.
Abra um terminal e execute dotnet new mvc -n MailDemo. Esse comando cria um novo projeto web .NET usando uma arquitetura MVC dentro de uma nova pasta /MailDemo. Em seguida, execute cd MailDemo para navegar até a nova pasta criada para você.
Depois de navegar para a pasta, você precisa adicionar os seus valores de configuração ao appsettings.json. Adicione uma nova entrada JSON chamada “Mailgun”. O valor para Mailgun:ApiKey é a chave de API que você criou na seção anterior (você aprenderá a proteger suas chaves de API mais adiante). Mailgun:Domain é o domínio sandbox que já foi criado para você.
Todo o seu arquivo appsettings.json deve ficar assim:
{rn "Logging": {rn "LogLevel": {rn "Default": "Information",rn "Microsoft.AspNetCore": "Warning"rn }rn },rn "AllowedHosts": "*",rn "Mailgun": {rn "ApiKey": "<Your API Key>",rn "Domain": "<Your sandbox domain>"rn }rn}
Em seguida, você precisa configurar um HttpClient nomeado para facilitar a reutilização de parâmetros padrão. Para fazer isso, abra o Program.cs. A parte superior do arquivo deve ficar assim:
using System.Net.Http.Headers;rnusing System.Text;rnrnvar builder = WebApplication.CreateBuilder(args);rnrn// Add services to the containerrnbuilder.Services.AddControllersWithViews();rnrn// Set up your named HttpClient for easy reusernbuilder.Services.AddHttpClient("Mailgun", client =>rn{rn // Grab values from the configurationrn var apiKey = builder.Configuration.GetValue<string>("Mailgun:ApiKey");rn var base64Auth = Convert.ToBase64String(Encoding.ASCII.GetBytes($"api:{apiKey}"));rn var domain = builder.Configuration.GetValue<string>("Mailgun:Domain");rnrn // Set default values on the HttpClientrn client.BaseAddress = new Uri($"https://api.mailgun.net/v3/{domain}/messages");rn client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64Auth);rn});rnrnvar app = builder.Build();rn
Como você enviará apenas e-mails, o seu HttpClient é configurado com a URL completa da API HTTP do Mailgun, que é usada para envio de e-mail.
Neste ponto, tudo o que você precisa para enviar o seu primeiro e-mail está pronto.
Mais sobre os endpoints da API do Mailgun
O HttpClient é configurado para enviar solicitações HTTP para o endpoint https://api.mailgun.net/v3/{domain}/messages. O Mailgun também oferece outros endpoints para ajudar você a ter uma ferramenta robusta e holística para gerenciar e enviar e-mails transacionais:
- Enviar e-mail: POST /v3/{domain_name}/messages
- Enviar e-mail no formato MIME: POST /v3/{domain_name}/messages.mime
- Recuperar um e-mail armazenado: GET /v3/domains/{domain_name}/messages/{storage_key}
- Ver seus domínios: GET /v4/domains
- Criar um novo domínio: POST /v4/domains
- Ver uma lista paginada de todos os eventos de entrada e de saída: GET /v3/{domain_name}/events
- Ver uma lista paginada de todas as devoluções: GET /v3/{domainID}/bounces
Enviar o seu primeiro e-mail com a API do Mailgun
Antes de enviar o seu primeiro e-mail transacional, certifique-se de que o endereço de e-mail do destinatário usado seja um dos destinatários autorizados para o seu domínio sandbox. Na sua conta do Mailgun, veja seus domínios, abra o seu domínio sandbox e, no lado direito, adicione seu e-mail pessoal como um destinatário autorizado:

Em seguida, crie um novo arquivo em /Controllers/OrderController.cs e substitua o conteúdo do arquivo pelo seguinte:
using System.Diagnostics;rnusing Microsoft.AspNetCore.Mvc;rnusing MailDemo.Models;rnusing System.Text;rnusing System.Net.Mime;rnrnnamespace MailDemo.Controllers;rnrnpublic class OrderController : Controllerrn{rn private readonly HttpClient _httpClient;rn private readonly IConfiguration _config;rnrn public OrderController(IHttpClientFactory httpClientFactory, IConfiguration config)rn {rn // Get your named HttpClient that is preconfiguredrn this._httpClient = httpClientFactory.CreateClient("Mailgun");rn this._config = config;rn }rnrn public async Task<IActionResult> Confirm()rn {rn using MultipartFormDataContent form = new();rnrn // Local function keeps this code a bit clearerrn void SetFormParam(string key, string value) =>rn form.Add(new StringContent(value, Encoding.UTF8, MediaTypeNames.Text.Plain), key);rnrn SetFormParam("from", $"Test User <postmaster@{this._config.GetValue<string>("Mailgun:Domain")}>");rn SetFormParam("to", "your_authorized_recipient.com");rn SetFormParam("subject", "Hello World!");rn SetFormParam("text", "My first transactional email!");rn SetFormParam("html", @"<html><body><p style=""color:blue;"">My first transactional email!</p></body></html>");rnrn var result = await this._httpClient.PostAsync(string.Empty, form);rnrn if (!result.IsSuccessStatusCode)rn {rn return new JsonResult(await result.Content.ReadAsStringAsync());rn }rnrn return new JsonResult(@"Your order was confirmed. You should get an email soon!");rn }rn}rn
Execute dotnet run do seu terminal para iniciar a sua aplicação web. Depois, abra um navegador web e navegue até /order/confirm. Você deve receber um e-mail na sua caixa de entrada em alguns segundos:

Lidar com respostas e erros
Infelizmente, às vezes, as coisas dão errado. Por exemplo, a sua chave de API pode ter um erro de digitação, talvez você não tenha adicionado todos os campos obrigatórios à solicitação HTTP, ou você pode ter atingido os limites de taxa do Mailgun.
Uma solicitação HTTP com falha para a API do Mailgun retornará um de quatro códigos de erro HTTP diferentes:
- 400: Algo na formatação da solicitação está errado.
- 401: Falha na autenticação.
- 429: Você atingiu o limite de taxa do Mailgun.
- 500: Algo na rede ou no lado do Mailgun deu errado.
Vamos ver como você pode lidar com alguns desses cenários de falha.
Lidar com códigos de erro 400
Sempre que você receber um código de status HTTP 400, significa que o seu payload ou a sua formatação estava incorreta. Por exemplo, o Mailgun responderá com um código de status HTTP 400 se um campo obrigatório estiver ausente.
Nesses casos, existe uma abordagem geral que você pode adotar:
- Lançar uma exceção, pois esse é um cenário de falha não recuperável.
- Registrar a exceção no log e os motivos para a exceção.
Veja como o código para isso pode ficar:
if (!result.IsSuccessStatusCode)rn{rn var status = (int) result.StatusCode;rnrn if (status == 400)rn {rn var responseContent = await result.Content.ReadAsStringAsync();rn var exception = new BadHttpRequestException("Mailgun 400 HTTP status code");rnrn this._logger.LogError(exception, exception.Message, responseContent);rn throw exception;rn }rn}rn
Você também pode adotar uma abordagem semelhante com o código de status HTTP 401.
Lidar com códigos de erro 500
Sempre que você receber um código de status HTTP 500, isso pode significar que algo deu errado no lado do Mailgun ou que ocorreu um problema geral de rede. Nesses casos, existem algumas abordagens diferentes que você pode adotar:
- Colocar o e-mail na fila para ser tentado de novo mais tarde.
- No mesmo caminho do código, tentar de novo após um curto período.
- Empregar uma técnica avançada, como um circuit breaker
Veja como uma abordagem de nova tentativa básica pode ficar:
if (!result.IsSuccessStatusCode)rn{rn var status = (int) result.StatusCode;rnrn if (status == 500)rn {rn // Retry the email after 1 second in hopes that the error was transientrnrn await Task.Delay(1000);rn result = await _httpClient.PostAsync(string.Empty, form);rnrn if (!result.IsSuccessStatusCode)rn {rn // Log and throw an exception like you did in the previous examplern }rn }rn}rn
Nesse cenário, se você receber um código de status 500, esperará um segundo e tentará fazer a solicitação HTTP de novo. Se a solicitação repetida também tiver uma falha, você registrará no log e lançará uma exceção da mesma forma que fez para o cenário do código de status 400.
Para dar uma ideia de como o código geral pode ficar, aqui está um esqueleto de como seu código poderia começar a lidar com vários cenários de erro:
if (!result.IsSuccessStatusCode)rn{rn var status = (int)result.StatusCode;rnrn if (status == 400)rn {rn // Log the error and notify the development team that thern // request was not formatted properly.rn }rn else if (status == 401)rn {rn // Log the error and notify the development team that thern // request's authentication failed.rn }rn else if (status == 429)rn {rn // Use a .NET library like Polly to throttle or try this requestrn // again with an exponential back-off, etc.rn }rn elsern {rn // Something went wrong: log the error and apply a distributed rn // error handling technique like a circuit breaker.rn // See https://learn.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker for more.rn }rn}rn
Integração de e-mail pronta para produção
Neste ponto, você já enviou um e-mail com .NET e C#, mas, para ficar pronto para produção, há algumas coisas importantes a considerar.
Proteger a sua chave de API
Você não pode armazenar a sua chave de API de produção real no código-fonte. Além disso, ela não deve ser acessível por ninguém em arquivos de texto simples.
Existem muitas ferramentas que você pode usar para proteger a sua chave de API. Todo provedor de nuvem tem seu próprio sistema de gerenciamento de chaves, que permite que a sua aplicação recupere seus segredos com segurança. Por exemplo, Azure Key Vault ou AWS Secrets Manager funcionam muito bem.
Outros recursos de nível de produção
O Mailgun também oferece suporte a muitos recursos de nível de produção dos quais você pode precisar, como:
- Adicionar anexos aos seus e-mails
- A capacidade de criar modelos de e-mail reutilizáveis
- Recursos de personalização, como variáveis e tags
- Cabeçalhos CC, BCC e reply-to
Integrar-se com o ecossistema .NET
Para demonstrar como é fácil a integração com ferramentas prontas para produção do ecossistema .NET, vá em frente e use uma biblioteca de código aberto como a Coravel para aprimorar a reutilização e a experiência de desenvolvimento da sua solução.
Execute dotnet add package coravel.mailer do seu terminal enquanto estiver na pasta MailDemo.
Depois, adicione a seguinte entrada JSON ao seu arquivo appsettings.json:
"Coravel": {rn "Mail": {rn "From": {rn "Name": "Test User",rn "Email": "postmaster@<Your sandbox domain>"rn }rn }rn}rnNext, create a class file called MailgunMailer.cs that implements the ICanSendMail interface. You might notice that this is essentially the same logic you originally had in OrderController:rnusing System.Net.Mime;rnusing System.Text;rnusing Coravel.Mailer.Mail;rnrnnamespace MailDemo;rnrnpublic class MailgunMailer : ICanSendMailrn{rn private readonly HttpClient _httpClient;rn private readonly IConfiguration _config;rnrn public MailgunMailer(IHttpClientFactory httpClientFactory, IConfiguration config)rn {rn this._httpClient = httpClientFactory.CreateClient("Mailgun");rn this._config = config;rn }rnrn public async Task SendAsync(MessageBody message, string subject, IEnumerable<MailRecipient> to, MailRecipient from, MailRecipient replyTo, IEnumerable<MailRecipient> cc, IEnumerable<MailRecipient> bcc, IEnumerable<Attachment>? attachments = null, MailRecipient? sender = null)rn {rn using MultipartFormDataContent form = new();rnrn void SetFormParam(string key, string value) =>rn form.Add(new StringContent(value, Encoding.UTF8, MediaTypeNames.Text.Plain), key);rnrn if (from is not null)rn {rn SetFormParam("from", $"{from.Name} <{from.Email}>");rn }rn elsern {rn // This gives you a default "from" field if not defined by the callerrn SetFormParam("from", $"{this._config.GetValue<string>("Coravel:Mail:From:Name")} <{this._config.GetValue<string>("Coravel:Mail:From:Email")}>");rn }rnrn foreach (var recipient in to)rn {rn SetFormParam("to", recipient.Email);rn }rn SetFormParam("subject", subject);rnrn if (message.HasHtmlMessage())rn {rn SetFormParam("html", message.Html);rn }rnrn if (message.HasPlainTextMessage())rn {rn SetFormParam("text", message.Text);rn }rnrn var result = await _httpClient.PostAsync(string.Empty, form);rnrn // Error handling logic...rn }rn}rn
Em seguida, você precisa informar ao Coravel para usar esse mailer. No seu arquivo Program.cs, antes de chamar var app = builder.Build(), adicione o seguinte:
builder.Services.AddScoped<MailDemo.MailgunMailer>();rnbuilder.AddCustomMailer<MailDemo.MailgunMailer>();rnFinally, replace the code for OrderController with the following:rnusing Microsoft.AspNetCore.Mvc;rnusing Coravel.Mailer.Mail.Interfaces;rnusing Coravel.Mailer.Mail;rnrnnamespace MailDemo.Controllers;rnrnpublic class OrderController : Controllerrn{rn private readonly IMailer _mailer;rnrn public OrderController(IMailer mailer)rn {rn this._mailer = mailer;rn }rnrn public async Task<IActionResult> Confirm()rn {rn var mailable = Mailable.AsInline()rn .To("jamesmichaelhickey@gmail.com")rn .Subject("Hello World!")rn .Html(@"<html><body><p style=""color:blue;"">My first transactional email!</p></body></html>")rn .Text("My first transactional email!");rnrn await _mailer.SendAsync(mailable);rnrn return new JsonResult(@"Your order was confirmed. You should get an email soon!");rn }rn}rn
Após um pouco de configuração única, você notará que a lógica para enviar e-mails a partir do código da sua aplicação é muito mais fácil de entender e menos verbosa.
Você também adicionou suporte, por meio do Mailgun e do Coravel, para definir corpos de e-mail em HTML e e-mail em texto simples. O Mailgun recomenda enviar tanto HTML quanto texto simples juntos. É melhor enviar e-mails multipart usando tanto texto quanto HTML, ou apenas texto. O envio de e-mail apenas em HTML não é bem recebido pelos ESPs.
Conclusão
Mailgun oferece uma maneira direta API HTTP de enviar e-mails das suas soluções .NET com eficiência e segurança. Neste artigo, você aprendeu a integrar a API HTTP do Mailgun a uma aplicação web C# e a começar a adicionar ferramentas de nível de produção junto com os recursos prontos para produção do Mailgun.
Ao usar o Mailgun e o .NET juntos, os seus e-mails transacionais podem escalar com eficiência com a funcionalidade async/await do .NET, toda a infraestrutura web de alto desempenho do framework, ferramentas adicionais do ecossistema .NET e a API HTTP direta e de alto desempenho do Mailgun.
Isso foi útil? Assine a nossa newsletter para receber atualizações sobre tutoriais, notícias sobre e-mails e insights de nossos especialistas em e-mail residentes.