c# - template - ¿Puedo configurar plantillas HTML/Email con ASP.NET?
send email html template c# (23)
Lo ideal sería utilizar una página .ASPX como plantilla de alguna manera, y luego indicarle a mi código que publique esa página, y usar el código HTML devuelto para el correo electrónico.
Simplemente podría construir una WebRequest para acceder a una página ASPX y obtener el HTML resultante. Con un poco más de trabajo, probablemente pueda hacerlo sin la WebRequest. Un PageParser y un Response.Filter le permiten ejecutar la página y capturar la salida ... aunque puede haber algunas formas más elegantes.
Estoy trabajando en un sitio que enviará una gran cantidad de correos electrónicos. Quiero configurar tanto el texto de encabezado y pie de página, o incluso las plantillas para permitir a los usuarios editar fácilmente estos correos electrónicos si es necesario.
Si inserto el HTML dentro de los literales de cadena C #, es feo y tendrían que preocuparse de escapar. Incluir archivos planos para el encabezado y el pie de página podría funcionar, pero algo al respecto simplemente no parece correcto.
Lo ideal sería utilizar una página .ASPX
como plantilla de alguna manera, y luego indicarle a mi código que publique esa página, y usar el código HTML devuelto para el correo electrónico.
¿Hay alguna manera agradable y fácil de hacer esto? ¿Hay una mejor manera de resolver este problema?
Actualizado:
Agregué una respuesta que le permite usar una página .aspx estándar como plantilla de correo electrónico. Simplemente reemplace todas las variables como lo haría normalmente, utilice databinding, etc. Luego solo capture la salida de la página y ¡listo! ¡Tienes tu correo HTML!
ACTUALIZADO CON CAVEAT !!!:
Estaba usando la clase MailDefinition en algunas páginas aspx, pero al intentar usar esta clase durante un proceso de servidor que se estaba ejecutando, falló. Creo que fue porque el método MailDefinition.CreateMailMessage () requiere un control válido para hacer referencia, aunque no siempre hace algo. Debido a esto, recomendaría mi enfoque usando una página aspx, o el enfoque de Mun usando una página ascx, que parece un poco mejor.
@bardev proporciona una buena solución, pero desafortunadamente no es ideal en todos los casos. El mío fue uno de ellos.
Estoy usando WebForms en un sitio web (juro que nunca volveré a usar un sitio web, qué PITA) en VS 2013.
Probé la sugerencia de Razor, pero siendo un sitio web no recibí el todo-importante IntelliSense que el IDE entrega en un proyecto de MVC. También me gusta usar el diseñador para mis plantillas, un lugar perfecto para un UserControl.
Nix en Razor de nuevo.
Así que se me ocurrió este pequeño marco (consejos de sombrero para @mun para UserControl y @imatoria para Strong Typing). Casi el único problema potencial que veo es que debe tener cuidado de mantener su nombre de archivo .ASCX sincronizado con su nombre de clase. Si te alejas, obtendrás un error de tiempo de ejecución.
FWIW: En mi prueba, al menos, la llamada RenderControl () no le gusta el control de página, así que fui con UserControl.
Estoy bastante seguro de haber incluido todo aquí; avísame si dejé algo fuera.
HTH
Uso:
Partial Class Purchase
Inherits UserControl
Private Sub SendReceipt()
Dim oTemplate As MailTemplates.PurchaseReceipt
oTemplate = MailTemplates.Templates.PurchaseReceipt(Me)
oTemplate.Name = "James Bond"
oTemplate.OrderTotal = 3500000
oTemplate.OrderDescription = "Q-Stuff"
oTemplate.InjectCss("PurchaseReceipt")
Utils.SendMail("{0} <[email protected]>".ToFormat(oTemplate.Name), "Purchase Receipt", oTemplate.ToHtml)
End Sub
End Class
Clase base:
Namespace MailTemplates
Public MustInherit Class BaseTemplate
Inherits UserControl
Public Shared Function GetTemplate(Caller As TemplateControl, Template As Type) As BaseTemplate
Return Caller.LoadControl("~/MailTemplates/{0}.ascx".ToFormat(Template.Name))
End Function
Public Sub InjectCss(FileName As String)
If Me.Styler IsNot Nothing Then
Me.Styler.Controls.Add(New Controls.Styler(FileName))
End If
End Sub
Private ReadOnly Property Styler As PlaceHolder
Get
If _Styler Is Nothing Then
_Styler = Me.FindNestedControl(GetType(PlaceHolder))
End If
Return _Styler
End Get
End Property
Private _Styler As PlaceHolder
End Class
End Namespace
Clase "Factory":
Namespace MailTemplates
Public Class Templates
Public Shared ReadOnly Property PurchaseReceipt(Caller As TemplateControl) As PurchaseReceipt
Get
Return BaseTemplate.GetTemplate(Caller, GetType(PurchaseReceipt))
End Get
End Property
End Class
End Namespace
Clase de plantilla:
Namespace MailTemplates
Public MustInherit Class PurchaseReceipt
Inherits BaseTemplate
Public MustOverride WriteOnly Property Name As String
Public MustOverride WriteOnly Property OrderTotal As Decimal
Public MustOverride WriteOnly Property OrderDescription As String
End Class
End Namespace
Encabezado ASCX:
<%@ Control Language="VB" ClassName="_Header" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!--
See https://www.campaignmonitor.com/blog/post/3317/ for discussion of DocType in HTML Email
-->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<asp:PlaceHolder ID="plcStyler" runat="server"></asp:PlaceHolder>
</head>
<body>
Pie de página ASCX:
<%@ Control Language="VB" ClassName="_Footer" %>
</body>
</html>
Plantilla ASCX:
<%@ Control Language="VB" AutoEventWireup="false" CodeFile="PurchaseReceipt.ascx.vb" Inherits="PurchaseReceipt" %>
<%@ Register Src="_Header.ascx" TagName="Header" TagPrefix="uc" %>
<%@ Register Src="_Footer.ascx" TagName="Footer" TagPrefix="uc" %>
<uc:Header ID="ctlHeader" runat="server" />
<p>Name: <asp:Label ID="lblName" runat="server"></asp:Label></p>
<p>Order Total: <asp:Label ID="lblOrderTotal" runat="server"></asp:Label></p>
<p>Order Description: <asp:Label ID="lblOrderDescription" runat="server"></asp:Label></p>
<uc:Footer ID="ctlFooter" runat="server" />
ASCX Template CodeFile:
Partial Class PurchaseReceipt
Inherits MailTemplates.PurchaseReceipt
Public Overrides WriteOnly Property Name As String
Set(Value As String)
lblName.Text = Value
End Set
End Property
Public Overrides WriteOnly Property OrderTotal As Decimal
Set(Value As Boolean)
lblOrderTotal.Text = Value
End Set
End Property
Public Overrides WriteOnly Property OrderDescription As Decimal
Set(Value As Boolean)
lblOrderDescription.Text = Value
End Set
End Property
End Class
Ayudantes:
''
'' FindNestedControl helpers based on tip by @andleer
'' at http://.com/questions/619449/
''
Public Module Helpers
<Extension>
Public Function AllControls(Control As Control) As List(Of Control)
Return Control.Controls.Flatten
End Function
<Extension>
Public Function FindNestedControl(Control As Control, Id As String) As Control
Return Control.Controls.Flatten(Function(C) C.ID = Id).SingleOrDefault
End Function
<Extension>
Public Function FindNestedControl(Control As Control, Type As Type) As Control
Return Control.Controls.Flatten(Function(C) C.GetType = Type).SingleOrDefault
End Function
<Extension>
Public Function Flatten(Controls As ControlCollection) As List(Of Control)
Flatten = New List(Of Control)
Controls.Traverse(Sub(Control) Flatten.Add(Control))
End Function
<Extension>
Public Function Flatten(Controls As ControlCollection, Predicate As Func(Of Control, Boolean)) As List(Of Control)
Flatten = New List(Of Control)
Controls.Traverse(Sub(Control)
If Predicate(Control) Then
Flatten.Add(Control)
End If
End Sub)
End Function
<Extension>
Public Sub Traverse(Controls As ControlCollection, Action As Action(Of Control))
Controls.Cast(Of Control).ToList.ForEach(Sub(Control As Control)
Action(Control)
If Control.HasControls Then
Control.Controls.Traverse(Action)
End If
End Sub)
End Sub
<Extension()>
Public Function ToFormat(Template As String, ParamArray Values As Object()) As String
Return String.Format(Template, Values)
End Function
<Extension()>
Public Function ToHtml(Control As Control) As String
Dim oSb As StringBuilder
oSb = New StringBuilder
Using oSw As New StringWriter(oSb)
Using oTw As New HtmlTextWriter(oSw)
Control.RenderControl(oTw)
Return oSb.ToString
End Using
End Using
End Function
End Module
Namespace Controls
Public Class Styler
Inherits LiteralControl
Public Sub New(FileName As String)
Dim _
sFileName,
sFilePath As String
sFileName = Path.GetFileNameWithoutExtension(FileName)
sFilePath = HttpContext.Current.Server.MapPath("~/Styles/{0}.css".ToFormat(sFileName))
If File.Exists(sFilePath) Then
Me.Text = "{0}<style type=""text/css"">{0}{1}</style>{0}".ToFormat(vbCrLf, File.ReadAllText(sFilePath))
Else
Me.Text = String.Empty
End If
End Sub
End Class
End Namespace
Public Class Utils
Public Shared Sub SendMail(Recipient As MailAddress, Subject As String, HtmlBody As String)
Using oMessage As New MailMessage
oMessage.To.Add(Recipient)
oMessage.IsBodyHtml = True
oMessage.Subject = Subject.Trim
oMessage.Body = HtmlBody.Trim
Using oClient As New SmtpClient
oClient.Send(oMessage)
End Using
End Using
End Sub
End Class
Aquí hay una alternativa más que usa transformaciones XSL para plantillas de correo electrónico más complejas: Envío de correo electrónico basado en HTML desde aplicaciones .NET .
Claro que puedes crear una plantilla html y yo recomendaría también una plantilla de texto. En la plantilla, puede colocar [CUERPO] en el lugar donde se colocaría el cuerpo y luego puede leer en la plantilla y reemplazar el cuerpo con el contenido nuevo. Puede enviar el correo electrónico usando .Nets Mail Class. Solo tiene que repetir el envío del correo electrónico a todos los destinatarios después de crear el correo electrónico inicialmente. Funcionó como un encanto para mí.
using System.Net.Mail;
// Email content
string HTMLTemplatePath = @"path";
string TextTemplatePath = @"path";
string HTMLBody = "";
string TextBody = "";
HTMLBody = File.ReadAllText(HTMLTemplatePath);
TextBody = File.ReadAllText(TextTemplatePath);
HTMLBody = HTMLBody.Replace(["[BODY]", content);
TextBody = HTMLBody.Replace(["[BODY]", content);
// Create email code
MailMessage m = new MailMessage();
m.From = new MailAddress("[email protected]", "display name");
m.To.Add("[email protected]");
m.Subject = "subject";
AlternateView plain = AlternateView.CreateAlternateViewFromString(_EmailBody + text, new System.Net.Mime.ContentType("text/plain"));
AlternateView html = AlternateView.CreateAlternateViewFromString(_EmailBody + body, new System.Net.Mime.ContentType("text/html"));
mail.AlternateViews.Add(plain);
mail.AlternateViews.Add(html);
SmtpClient smtp = new SmtpClient("server");
smtp.Send(m);
Creo que la respuesta fácil es MvcMailer. Es un paquete NuGet que le permite usar su motor de vista favorito para generar correos electrónicos. Vea el paquete NuGet here y la documentación del proyecto
¡Espero eso ayude!
Creo que también podrías hacer algo como esto:
Cree y .aspx página, y ponga esto al final del método OnLoad, o llámelo manualmente.
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
HtmlTextWriter htmlTW = new HtmlTextWriter(sw);
this.Render(htmlTW);
No estoy seguro de si hay algún problema potencial con esto, pero parece que funcionaría. De esta forma, podría usar una página .aspx con todas las funciones, en lugar de la clase MailDefinition, que solo admite reemplazos de texto.
DotLiquid es otra opción. Especifica valores de un modelo de clase como {{ user.name }}
y luego, en tiempo de ejecución, proporciona los datos de esa clase y la plantilla con el marcado, y combinará los valores por usted. Es similar al uso del motor de plantillas Razor de muchas maneras. Es compatible con cosas más complejas como bucles y varias funciones como ToUpper. Lo bueno es que estos son "seguros" para que el usuario que crea las plantillas no pueda bloquear su sistema o escribir un código inseguro como lo haría en razor: http://dotliquidmarkup.org/try-online
Es posible que también desee intentar cargar un control y luego representarlo en una cadena y configurarlo como el cuerpo HTML:
// Declare stringbuilder to render control to
StringBuilder sb = new StringBuilder();
// Load the control
UserControl ctrl = (UserControl) LoadControl("~/Controls/UserControl.ascx");
// Do stuff with ctrl here
// Render the control into the stringbuilder
StringWriter sw = new StringWriter(sb);
Html32TextWriter htw = new Html32TextWriter(sw);
ctrl.RenderControl(htw);
// Get full body text
string body = sb.ToString();
A continuación, puede construir su correo electrónico como de costumbre:
MailMessage message = new MailMessage();
message.From = new MailAddress("[email protected]", "from name");
message.Subject = "Email Subject";
message.Body = body;
message.BodyEncoding = Encoding.ASCII;
message.IsBodyHtml = true;
SmtpClient smtp = new SmtpClient("server");
smtp.Send(message);
El control del usuario puede contener otros controles, como un encabezado y pie de página, y también aprovechar la funcionalidad, como el enlace de datos.
Esta es una forma simple de usar la clase WebClient :
public static string GetHTMLBody(string url)
{
string htmlBody;
using (WebClient client = new WebClient ())
{
htmlBody = client.DownloadString(url);
}
return htmlBody;
}
Entonces solo llámalo así:
string url = "http://www.yourwebsite.com";
message.Body = GetHTMLBody(url);
Por supuesto, su CSS deberá alinearse para mostrar los estilos de la página web en la mayoría de los clientes de correo electrónico (como Outlook). Si su correo electrónico muestra contenido dinámico (por ejemplo, Nombre del cliente), le recomendaría utilizar QueryStrings en su sitio web para completar los datos. (ej. http://www.sitioweb.com?CustomerName=Bob )
Establezca el conjunto del mensaje de correo electrónico IsBodyHtml = true
Tome su objeto que contiene los contenidos de su correo electrónico Serialice el objeto y use xml / xslt para generar el contenido html.
Si desea hacer AlternateViews, haga lo mismo que jmein solo use una plantilla xslt diferente para crear el contenido de texto sin formato.
Una de las principales ventajas de esto es que si desea cambiar su diseño, todo lo que tiene que hacer es actualizar la plantilla xslt.
Me gusta la respuesta de Raj. Programas como ListManager y frameworks como DNN hacen cosas similares, y si se requiere una fácil edición por parte de usuarios no técnicos, los editores WYSIWYG para modificar el HTML almacenado en SQL son fáciles de manejar y pueden acomodar fácilmente encabezados de edición independientemente de los pies de página. etc., así como el uso de tokens para insertar valores dinámicamente.
Una cosa a tener en cuenta si se usa el método anterior (o alguno, realmente) es ser estricto y cuidadoso con respecto a los tipos de estilo y etiquetas que permite a los editores insertar. Si cree que los navegadores son quisquillosos, solo espere hasta que vea cómo los clientes de correo electrónico procesan de manera diferente lo mismo ...
Mira SubSonic (www.subsonicproject.com). Están haciendo exactamente esto para generar código: la plantilla es ASPX estándar y genera c #. El mismo método sería reutilizable para su escenario.
Podría probar la clase MailDefinition
Si desea pasar parámetros como nombres de usuario, nombres de productos, ... etc., puede usar el motor de plantillas de código abierto NVelocity para producir su correo electrónico / HTML final.
Un ejemplo de la plantilla de NVelocity ( MailTemplate.vm ):
A sample email template by <b>$name</b>.
<br />
Foreach example :
<br />
#foreach ($item in $itemList)
[Date: $item.Date] Name: $item.Name, Value: $itemValue.Value
<br /><br />
#end
Generando cuerpo de correo por MailTemplate.vm en su aplicación:
VelocityContext context = new VelocityContext();
context.Put("name", "ScarletGarden");
context.Put("itemList", itemList);
StringWriter writer = new StringWriter();
Velocity.MergeTemplate("MailTemplate.vm", context, writer);
string mailBody = writer.GetStringBuilder().ToString();
El cuerpo del correo resultante es:
Una muestra de plantilla de correo electrónico de ScarletGarden .
Ejemplo de Foreach:
[Fecha: 12.02.2009] Nombre: artículo 1, valor: 09
[Fecha: 21.02.2009] Nombre: artículo 4, valor: 52
[Fecha: 01.03.2009] Nombre: artículo 2, valor: 21
[Fecha: 23.03.2009] Nombre: artículo 6, valor: 24
Para editar las plantillas, quizás pueda usar FCKEditor y guardar sus plantillas en archivos.
Si la flexibilidad es uno de sus requisitos previos, XSLT podría ser una buena opción, que es completamente compatible con .NET Framework y usted podría incluso permitir que el usuario edite esos archivos. Este artículo ( http://www.aspfree.com/c/a/XML/XSL-Transformations-using-ASP-NET/ ) puede ser útil para empezar (msdn tiene más información al respecto). Como dijo ScarletGarden, NVelocity es otra buena opción, pero prefiero XSLT por su compatibilidad con el framework .NET "incorporado" y por su plataforma independiente.
Si puede permitir que ASPNET y los usuarios asociados tengan permiso para leer y escribir un archivo, puede usar fácilmente un archivo HTML con marcadores de posición estándar String.Format()
( {0}
, {1:C}
, etc.) para lograr esta.
Simplemente lea en el archivo, como una cadena, usando clases del espacio de nombres System.IO
. Una vez que tenga esa cadena, páselo como primer argumento a String.Format()
y proporcione los parámetros.
Mantenga ese hilo y utilícelo como el cuerpo del correo electrónico, y ya está hecho. Hacemos esto hoy en docenas de sitios (ciertamente pequeños) y no hemos tenido problemas.
Debo señalar que esto funciona mejor si (a) no está enviando tropecientos correos electrónicos a la vez, (b) no está personalizando cada correo electrónico (de lo contrario, consume una tonelada de cadenas) y (c) ) el archivo HTML en sí es relativamente pequeño.
Similar a la respuesta de Canavar, pero en lugar de NVelocity, siempre utilizo " StringTemplate ", que carga la plantilla desde un archivo de configuración, o cargo un archivo externo usando File.ReadAllText () y establezco los valores.
Es un proyecto de Java pero el puerto de C # es sólido y lo he usado en varios proyectos (solo lo usé para plantillas de correo electrónico usando la plantilla en un archivo externo).
Las alternativas son siempre buenas.
Tenía un requisito similar en 1 de los proyectos en los que tenía que enviar una gran cantidad de correos electrónicos cada día, y el cliente quería un control total sobre las plantillas html para diferentes tipos de correos electrónicos.
debido a la gran cantidad de correos electrónicos que se enviarán, el rendimiento fue una preocupación principal.
lo que se nos ocurrió fue contenido estático en el servidor sql donde guarda el marcado completo de la plantilla html (junto con marcadores de posición, como [UserFirstName], [UserLastName] que se reemplazan por datos reales en tiempo de ejecución) para diferentes tipos de correos electrónicos
luego cargamos estos datos en caché asp.net, por lo que no leemos las plantillas html una y otra vez, sino solo cuando realmente se cambian
le dimos al cliente un editor WYSIWYG para modificar estas plantillas a través de un formulario web de administración. cada vez que se hacían actualizaciones, reiniciamos asp.net cache.
y luego teníamos una tabla separada para los registros de correo electrónico, donde se registraba cada correo electrónico que se enviaba. esta tabla tenía campos llamados emailType, emailSent y numberOfTries.
simplemente ejecutamos un trabajo cada 5 minutos para tipos de correo electrónico importantes (como el registro de nuevo miembro, contraseña olvidada) que deben enviarse lo antes posible.
ejecutamos otro trabajo cada 15 minutos para tipos de correo menos importantes (como correo electrónico de promoción, correo electrónico de noticias, etc.)
De esta forma, no bloquea su servidor enviando correos electrónicos sin interrupción y procesa los correos en lotes. una vez que se envía un correo electrónico, configura el campo emailSent en 1.
Tenga cuidado al hacer esto, los filtros de SPAM parecen bloquear el html generado por ASP.net, aparentemente debido a ViewState, por lo que si va a hacer esto, asegúrese de que el HTML producido esté limpio.
Yo personalmente consideraría usar Asp.net MVC para lograr los resultados deseados. o NVelocity es bastante bueno en esto
Tenga en cuenta que las soluciones aspx y ascx requieren un HttpContext actual, por lo que no se pueden usar de forma asíncrona (por ejemplo, en subprocesos) sin mucho trabajo.
Usaría una biblioteca de plantillas como TemplateMachine . esto le permite colocar su plantilla de correo electrónico junto con texto normal y luego usar reglas para inyectar / reemplazar valores según sea necesario. Muy similar a ERB en Ruby. Esto le permite separar la generación del contenido del correo sin atarlo demasiado a algo como ASPX, etc. luego, una vez que el contenido se genera con esto, puede enviar por correo electrónico.
Ya hay un montón de respuestas aquí, pero me topé con un excelente artículo sobre cómo usar Razor con plantillas de correo electrónico. Razor fue empujado con ASP.NET MVC 3, pero MVC no está obligado a usar Razor. Este es un proceso bastante elegante de hacer plantillas de correo electrónico
Como identifica el artículo, "Lo mejor de Razor es que, a diferencia de su predecesor (formularios web), no está vinculado con el entorno web, podemos alojarlo fácilmente fuera de la web y usarlo como motor de plantilla para varios propósitos".
Generación de correos electrónicos HTML con RazorEngine - Parte 01 - Introducción
Aprovechando las plantillas Razor fuera de ASP.NET: ¡Ya no son solo para HTML!
Plantillas de correo electrónico más inteligentes en ASP.NET con RazorEngine
Control de calidad similar
Templating utilizando la nueva API RazorEngine
El componente de correo electrónico Mail.dll incluye motor de plantilla de correo electrónico:
Aquí está la sintaxis general:
<html>
<body>
Hi {FirstName} {LastName},
Here are your orders:
{foreach Orders}
Order ''{Name}'' sent to <strong>{Street}</strong>.
{end}
</body>
</html>
Y el código que carga la plantilla, llena datos del objeto c # y envía un correo electrónico:
Mail.Html(Template
.FromFile("template.txt")
.DataFrom(_contact)
.Render())
.Text("This is text version of the message.")
.From(new MailBox("[email protected]", "Alice"))
.To(new MailBox("[email protected]", "Bob"))
.Subject("Your order")
.UsingNewSmtp()
.WithCredentials("[email protected]", "password")
.Server("mail.com")
.WithSSL()
.Send();
Puede obtener más información sobre la publicación de blog del motor de plantilla de correo electrónico .
O simplemente descargue el componente de correo electrónico Mail.dll y pruébelo.
Tenga en cuenta que este es un producto comercial que he creado.