¿Puedo configurar plantillas HTML / Email con ASP.NET?

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.

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 Stackoverflow similar

Templating utilizando la nueva API RazorEngine

Usando Razor sin MVC

¿Es posible usar Razor View Engine fuera de asp.net

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("from@email.com", "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.

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 $name. 
Foreach example :
#foreach ($item in $itemList) [Date: $item.Date] Name: $item.Name, Value: $itemValue.Value

#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.

El componente de correo electrónico Mail.dll incluye motor de plantilla de correo electrónico:

Aquí está la syntax general:

   Hi {FirstName} {LastName}, Here are your orders: {foreach Orders} Order '{Name}' sent to {Street}. {end}   

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("alice@mail.com", "Alice")) .To(new MailBox("bob@mail.com", "Bob")) .Subject("Your order") .UsingNewSmtp() .WithCredentials("alice@mail.com", "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.

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.

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("address@gmail.com", "display name"); m.To.Add("address@gmail.com"); 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 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.

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 .

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

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.

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.

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.

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.

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 aquí y la documentación del proyecto

¡Espero eso ayude!

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

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.

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.

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.

Me gusta la respuesta de Raj. Progtwigs 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 tags 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 …

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.

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 dynamic (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 )

@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} ".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" %>         

Pie de página ASCX:

 <%@ Control Language="VB" ClassName="_Footer" %>   

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" %>  

Name:

Order Total:

Order Description:

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://stackoverflow.com/questions/619449/ ' Public Module Helpers  Public Function AllControls(Control As Control) As List(Of Control) Return Control.Controls.Flatten End Function  Public Function FindNestedControl(Control As Control, Id As String) As Control Return Control.Controls.Flatten(Function(C) C.ID = Id).SingleOrDefault End Function  Public Function FindNestedControl(Control As Control, Type As Type) As Control Return Control.Controls.Flatten(Function(C) C.GetType = Type).SingleOrDefault End Function  Public Function Flatten(Controls As ControlCollection) As List(Of Control) Flatten = New List(Of Control) Controls.Traverse(Sub(Control) Flatten.Add(Control)) End Function  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  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  Public Function ToFormat(Template As String, ParamArray Values As Object()) As String Return String.Format(Template, Values) End Function  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}{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