NOTE: Apart from
(and even then it's questionable, I'm Scottish). These are machine translated in languages I don't read. If they're terrible please contact me.
You can see how this translation was done in this article.
Wednesday, 07 August 2024
//Less than a minute
Este es un artículo bastante simple, pero cubrirá algo de la odness de usar FluentEmail en ASP.NET Core para enviar correos HTML que no he visto en otra parte.
Enviar correos HTML es en sí mismo un poco simple con SmtpClient, pero no es muy flexible y no soporta cosas como plantillas o adjuntos. FluentEmail es una gran biblioteca para esto, pero no siempre está claro cómo usarlo en ASP.NET Core.
FluentEmail con Razorlight (está integrado) te permite plantillar tus emails usando la sintaxis de Razor. Esto es genial, ya que le permite utilizar todo el poder de Razor para crear sus correos electrónicos.
En primer lugar, es necesario instalar las bibliotecas FluentEmail.Core, FluentEmail.Smtp & FluentEmail.Razor:
dotnet add package FluentEmail.Core
dotnet add package FluentEmail.Smtp
dotnet add package FluentEmail.Razor
Para mantener las cosas separadas, creé una extensión de IServiceCollection que establece los servicios de FluentEmail:
namespace Mostlylucid.Email;
public static class Setup
{
public static void SetupEmail(this IServiceCollection services, IConfiguration config)
{
var smtpSettings = services.ConfigurePOCO<SmtpSettings>(config.GetSection(SmtpSettings.Section));
services.AddFluentEmail(smtpSettings.SenderEmail, smtpSettings.SenderName)
.AddRazorRenderer();
services.AddSingleton<ISender>(new SmtpSender( () => new SmtpClient()
{
DeliveryMethod = SmtpDeliveryMethod.Network,
Host = smtpSettings.Server,
Port = smtpSettings.Port,
Credentials = new NetworkCredential(smtpSettings.Username, smtpSettings.Password),
EnableSsl = smtpSettings.EnableSSL,
UseDefaultCredentials = false
}));
services.AddSingleton<EmailService>();
}
}
Configuración ##SMTP
Como verás, también utilicé el método IConfigSection mencionado en mi Artículo anterior para obtener la configuración SMTP.
var smtpSettings = services.ConfigurePOCO<SmtpSettings>(config.GetSection(SmtpSettings.Section));
Esto viene del archivo appsettings.json:
"SmtpSettings":
{
"Server": "smtp.gmail.com",
"Port": 587,
"SenderName": "Mostlylucid",
"Username": "",
"SenderEmail": "[email protected]",
"Password": "",
"EnableSSL": "true",
"EmailSendTry": 3,
"EmailSendFailed": "true",
"ToMail": "[email protected]",
"EmailSubject": "Mostlylucid"
}
Nota: Para Google SMTP si utiliza MFA (que usted *¿En serio? Si necesitas hacer un contraseña de aplicación para tu cuenta.
Para dev local, puede añadir esto a su archivo secrets.json:
Para el uso del docker composite normalmente lo incluirías en un archivo.env:
SMTPSETTINGS_USERNAME="[email protected]"
SMTPSETTINGS_PASSWORD="<MFA PASSWORD>" -- this is the app password you created
Luego en el archivo de composición docker se inyectan estas como variables env:
services:
mostlylucid:
image: scottgal/mostlylucid:latest
ports:
- 8080:8080
environment:
- SmtpSettings__UserName=${SMTPSETTINGS_USERNAME}
- SmtpSettings__Password=${SMTPSETTINGS_PASSWORD}
Tome una nota de la separación, ya que esto puede realmente lío con docker componer. Para comprobar lo que se inyecta se puede utilizar
docker compose config
Para mostrarte cómo se ve el archivo con estos inyectados.
Un problema con Fluent Email es que usted necesita agregar esto a su csproj
<PropertyGroup>
<PreserveCompilationContext>true</PreserveCompilationContext>
</PropertyGroup>
Esto es porque FluentEmail utiliza RazorLight que necesita esto para funcionar.
Para los archivos de plantilla, puede incluirlos en su proyecto como archivos de contenido o como lo hago en el contenedor Docker, copiar los archivos a la imagen final
FROM build AS publish
RUN dotnet publish "Mostlylucid.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
# Copy the Markdown directory
COPY ./Mostlylucid/Markdown /app/Markdown
COPY ./Mostlylucid/Email/Templates /app/Email/Templates
# Switch to a non-root user
USER $APP_UID
¡De acuerdo, de vuelta al código!
Ahora lo tenemos todo configurado podemos agregar el servicio de correo electrónico. Este es un servicio simple que toma una plantilla y envía un correo electrónico:
public class EmailService(SmtpSettings smtpSettings, IFluentEmail fluentEmail)
{
public async Task SendCommentEmail(string commenterEmail, string commenterName, string comment, string postSlug)
{
var commentModel = new CommentEmailModel
{
PostSlug = postSlug,
SenderEmail = commenterEmail,
SenderName = commenterName,
Comment = comment
};
await SendCommentEmail(commentModel);
}
public async Task SendCommentEmail(CommentEmailModel commentModel)
{
// Load the template
var templatePath = "Email/Templates/MailTemplate.template";
await SendMail(commentModel, templatePath);
}
public async Task SendContactEmail(ContactEmailModel contactModel)
{
var templatePath = "Email/Templates/ContactEmailModel.template";
await SendMail(contactModel, templatePath);
}
public async Task SendMail(BaseEmailModel model, string templatePath)
{
var template = await File.ReadAllTextAsync(templatePath);
// Use FluentEmail to send the email
var email = fluentEmail.UsingTemplate(template, model);
await email.To(smtpSettings.ToMail)
.SetFrom(smtpSettings.SenderEmail, smtpSettings.SenderName)
.Subject("New Comment")
.SendAsync();
}
}
Como se puede ver aquí tenemos dos métodos, uno para los comentarios y otro para el formulario de contacto (¡Envíame un correo! ). En esta aplicación te hago iniciar sesión para que pueda obtener el correo que es de (y para evitar el spam).
Realmente la mayor parte del trabajo se hace aquí:
var template = await File.ReadAllTextAsync(templatePath);
// Use FluentEmail to send the email
var email = fluentEmail.UsingTemplate(template, model);
await email.To(smtpSettings.ToMail)
.SetFrom(smtpSettings.SenderEmail, smtpSettings.SenderName)
.Subject("New Comment")
.SendAsync();
Aquí abrimos un archivo de plantilla, añadimos el modelo que contiene el contenido del correo electrónico, lo cargamos en FluentEmail y luego lo enviamos. La plantilla es un archivo Razor simple:
@model Mostlylucid.Email.Models.ContactEmailModel
<!DOCTYPE html>
<html class="dark">
<head>
<title>Comment Email</title>
</head>
<body>
<h1>Comment Email</h1>
<p>New comment from email @Model.SenderEmail name @Model.SenderName</p>
<p>Thank you for your comment on our blog post. We appreciate your feedback.</p>
<p>Here is your comment:</p>
<div>
@Raw( @Model.Comment)</div>
<p>Thanks,</p>
<p>The Blog Team</p>
</body>
</html>
Estos se almacenan como archivos.template en la carpeta Email/Templates. Usted puede utilizar archivos.cshtml pero causa un problema con la etiqueta @Raw en la plantilla (es una cosa de luz de afeitar).
Finalmente llegamos al controlador; es realmente bastante sencillo
[HttpPost]
[Route("submit")]
[Authorize]
public async Task<IActionResult> Submit(string comment)
{
var user = GetUserInfo();
var commentHtml = commentService.ProcessComment(comment);
var contactModel = new ContactEmailModel()
{
SenderEmail = user.email,
SenderName =user.name,
Comment = commentHtml,
};
await emailService.SendContactEmail(contactModel);
return PartialView("_Response", new ContactViewModel(){Email = user.email, Name = user.name, Comment = commentHtml, Authenticated = user.loggedIn});
return RedirectToAction("Index", "Home");
}
Aquí obtenemos la información del usuario, procesar el comentario (Utilizo un simple procesador Markdown con Markdig para convertir Markdown a HTML) y luego enviar el correo electrónico.