我可以使用ASP.NET设置HTML /电子邮件模板吗?

我正在一个网站上发送大量的电子邮件。 我希望设置页眉和页脚文本,甚至可以设置模板,以便用户在需要时轻松编辑这些电子邮件。

如果我在C#string文字中embeddedHTML,这是丑陋的,他们将不得不担心逃跑。 包括页眉和页脚的平面文件可能会起作用,但关于它的一些东西感觉不对。

以某种方式使用.ASPX页面作为模板是理想的,然后告诉我的代码服务该页面,并使用返回的电子邮件的HTML。

有没有一个很好的和简单的方法来做到这一点? 有没有更好的方法去解决这个问题?

更新:
我添加了一个答案,使您可以使用标准的.aspx页面作为电子邮件模板。 只要像通常那样replace所有的variables,使用数据绑定等,然后捕获页面的输出,瞧! 你有你的HTML电子邮件!

更新CAVEAT !!!:
我在一些aspx页面上使用了MailDefinition类,但是在运行的服务器进程中尝试使用这个类时,它失败了。 我相信这是因为MailDefinition.CreateMailMessage()方法需要一个有效的控件来引用,即使它并不总是做一些事情。 因此,我会推荐使用aspx页面的方法,或者使用ascx页面的方法,这似乎更好一些。

这里已经有很多答案,但是我偶然发现了一篇关于如何在电子邮件模板中使用Razor的好文章。 Razor被推到ASP.NET MVC 3,但MVC不需要使用Razor。 这是做电子邮件模板非常漂亮的处理

正如文章所指出的那样,“Razor最好的事情就是不像其前身(webforms),它不受networking环境的束缚,我们可以很容易地将其托pipe在networking之外,并将其作为模板引擎用于各种目的。

使用RazorEngine生成HTML电子邮件 – 第01部分 – 简介

利用ASP.NET之外的Razor模板:它们不仅适用于HTML!

使用RazorEngine在ASP.NET中更智能的电子邮件模板

类似的Stackoverflow QA

使用新的RazorEngine API进行模板化

使用没有MVC的Razor

是否有可能在asp.net之外使用Razor View Engine

您可能还想尝试加载控件,然后将其呈现为string并将其设置为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(); 

然后你可以照常构build你的电子邮件:

 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); 

用户控件可以包含其他控件,如页眉和页脚,还可以利用数据绑定等function。

你可以尝试MailDefinition类

如果你想传递用户名,产品名等参数,你可以使用开源模板引擎NVelocity来生成你最终的电子邮件/ HTML。

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 

在您的应用程序中通过MailTemplate.vm生成邮件正文:

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

结果邮件正文是:

ScarletGarden提供的示例电子邮件模板。

每个例子:

[date:12.02.2009]名称:项目1,值:09

[date:21.02.2009]名称:项目4,价值:52

[date:01.03.2009]名称:项目2,值:21

[date:23.03.2009]名称:第6项,价值:24

为了编辑模板,也许你可以使用FCKEditor并将你的模板保存到文件中。

Mail.dll电子邮件组件包括邮件模板引擎:

以下是语法概述:

 <html> <body> Hi {FirstName} {LastName}, Here are your orders: {foreach Orders} Order '{Name}' sent to <strong>{Street}</strong>. {end} </body> </html> 

加载模板的代码填充来自c#对象的数据并发送一封电子邮件:

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

您可以在电子邮件模板引擎博客文章中获得更多信息。

或者只是下载Mail.dll电子邮件组件,并尝试一下。

请注意,这是我创build的商业产品。

如果灵活性是您的先决条件之一,XSLT可能是一个不错的select,完全由.NET框架支持,您甚至可以让用户编辑这些文件。 这篇文章( http://www.aspfree.com/c/a/XML/XSL-Transformations-using-ASP-NET/ )可能对一开始是有用的(MSDN有关于它的更多信息)。 正如ScarletGarden所说,NVelocity是另一个不错的select,但我更喜欢XSLT的“内置”.NET框架支持和平台不可知论。

当然,你可以创build一个HTML模板,我也会build议一个文本模板。 在模板中,您可以将[BODY]放在身体放置的位置,然后您可以在模板中读取并用新内容replace身体。 您可以使用.Net邮件类发送电子邮件。 创build电子邮件后,您只需循环发送电子邮件给所有收件人。 为我工作就像一个魅力。

 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); 

我想你也可以做这样的事情:

创build和.aspx页面,并将其放在OnLoad方法的末尾,或手动调用它。

  StringBuilder sb = new StringBuilder(); StringWriter sw = new StringWriter(sb); HtmlTextWriter htmlTW = new HtmlTextWriter(sw); this.Render(htmlTW); 

我不确定是否有任何潜在的问题,但它看起来会起作用。 这样,您可以使用全function的.aspx页面,而不是仅支持文本replace的MailDefinition类。

以下是对更复杂的电子邮件模板使用XSL转换的另一种方法: 从.NET应用程序发送基于HTML的电子邮件

这样做时要小心,垃圾邮件filter似乎阻止ASP.net生成的HTML,显然是因为ViewState,所以如果你要这样做,确保生成的Html是干净的。

我个人会考虑使用Asp.net MVC来实现你想要的结果。 或者NVelocity在这方面相当不错

以某种方式使用.ASPX页面作为模板是理想的,然后告诉我的代码服务该页面,并使用返回的电子邮件的HTML。

你可以很容易地构build一个WebRequest来打一个ASPX页面并得到最终的HTML。 多做一点工作,可以在没有WebRequest的情况下完成。 一个PageParser和一个Response.Filter将允许你运行页面并捕获输出…虽然可能有一些更优雅的方式。

我对每天发送大量电子邮件的项目之一有类似的要求,客户希望完全控制不同types的电子邮件的HTML模板。

由于要发送大量的电子邮件,性能是主要关心的问题。

我们想出的是sql server中的静态内容,您可以在其中保存整个html模板标记(以及占位符,例如[UserFirstName],[UserLastName],在运行时用实际数据replace),用于不同types的电子邮件

然后我们加载这个数据在asp.netcaching – 所以我们不读一遍又一遍的HTML模板 – 但只有当他们实际上改变

我们给客户一个所见即所得的编辑器,通过pipe理员的网页forms修改这些模板。 无论何时更新,我们重置asp.netcaching。

然后我们有一个独立的电子邮件日志表 – 每个发送的电子邮件都被logging下来。 这个表有字段叫做emailType,emailSent和numberOfTries。

我们只需要每隔5分钟就为一些重要的电子邮件types(如新会员注册,忘记密码)等工作而急需一份工作

我们每隔15分钟就为不太重要的电子邮件types(例如促销邮件,新闻邮件等)

这样你不会阻止你的服务器发送不停的电子邮件,并且你批量处理邮件。 一旦发送了一封电子邮件,您将emailSent字段设置为1。

看看SubSonic(www.subsonicproject.com)。 他们正在做这个来生成代码 – 模板是标准的ASPX,它输出的C#。 同样的方法可以重复使用您的scheme。

请注意,aspx和ascx解决scheme需要一个当前的HttpContext,所以不能asynchronous使用(例如在线程中)而没有太多的工作。

我认为简单的答案是MvcMailer。 它是NuGet包,让您使用您最喜爱的视图引擎来生成电子邮件。 请参阅NuGet软件包和项目文档

希望能帮助到你!

DotLiquid是另一种select。 您可以将类模型中的值指定为{{ user.name }} ,然后在运行时提供该类中的数据以及带有标记的模板,并将合并这些值。 这与在很多方面使用Razor模板引擎相似。 它支持更复杂的东西像循环和ToUpper等各种function。 好的是这些是“安全的”,以便创build模板的用户不会像在razor中那样崩溃系统或编写不安全的代码: http : //dotliquidmarkup.org/try-online

如果您可以允许ASPNET和相关用户读取和写入文件的权限,则可以使用带有标准String.Format()占位符( {0}{1:C}等)的HTML文件轻松完成这个。

只需使用System.IO命名空间中的类以stringforms读入文件。 一旦你有了这个string,把它作为第一个parameter passing给String.Format() ,并提供参数。

保持这个string,并将其用作电子邮件的主体,并且基本完成。 我们今天在几十个(当然是小的)网站上这样做,而且没有问题。

我应该注意到,如果(a)你一次没有发送大量的电子邮件,(b)你没有对每个电子邮件进行个性化处理(否则你会吃掉大量的string)和(c )HTML文件本身相对较小。

设置电子邮件消息IsBodyHtml = true

带上包含电子邮件内容的对象序列化对象并使用xml / xslt生成html内容。

如果你想做AlternateViews做同样的事情,jmein只使用不同的xslt模板来创build纯文本内容。

这样做的主要优点之一是如果你想改变你的布局,你必须做更新xslt模板。

我会使用像TemplateMachine模板库。 这允许你主要把你的电子邮件模板和普通文本放在一起,然后根据需要使用规则来注入/replace值。 在Ruby中非常类似于ERB。 这样,您就可以将邮件内容的生成分开,而不必将您捆绑到ASPX之类的内容中,然后一旦生成内容,就可以通过电子邮件发送。

我喜欢拉吉的回答。 像DNN这样的ListManager和框架等程序做类似的事情,如果需要非技术用户的简单编辑,所见即所得的编辑器修改存储在SQL中的HTML是一个非常简单,直接的方式去,可以容易地容纳编辑标题独立于页脚,以及使用令牌dynamic插入值。

有一点要记住,如果使用上面的方法(或者其他任何方法),那么要严格谨慎地考虑编辑器允许插入哪些types的样式和标签。 如果你认为浏览器是挑剔的,只要等待,直到你看到不同的电子邮件客户端呈现相同的东西…

与Canavar的答案类似,但是我不是使用NVelocity,而是使用“ StringTemplate ”来从configuration文件加载模板,或者使用File.ReadAllText()加载外部文件并设置值。

这是一个Java项目,但C#端口是坚实的,我用它在几个项目(只用于电子邮件模板使用外部文件中的模板)。

替代品总是好的。

这是一个使用WebClient类的简单方法:

 public static string GetHTMLBody(string url) { string htmlBody; using (WebClient client = new WebClient ()) { htmlBody = client.DownloadString(url); } return htmlBody; } 

然后就这样调用它:

 string url = "http://www.yourwebsite.com"; message.Body = GetHTMLBody(url); 

当然,为了在大多数电子邮件客户端(例如Outlook)中显示网页的样式,您的CSS需要内联。 如果您的电子邮件显示dynamic内容(例如客户名称),那么我会build议在您的网站上使用QueryStrings填充数据。 (例如http://www.yourwebsite.com?CustomerName=Bob

@bardev提供了一个很好的解决scheme,但不幸的是,在所有情况下都不是很理想。 矿是其中之一。

我在网站中使用WebForms(我发誓我永远不会再使用一个网站 – 什么是PITA)在VS 2013。

我尝试了Razor的build议,但是我是一个网站,我没有得到IDE在MVC项目中提供的所有重要的IntelliSense。 我也喜欢使用我的模板devise器 – 一个UserControl的理想场所。

尼克在剃刀再次。

所以,我想出了这个小框架,而不是(用于UserControl的@mun和用于强types的@imatoria)。 我只能看到唯一的潜在问题,就是必须小心保持.ASCX文件名与其类名同步。 如果你stream浪了,你会得到一个运行时错误。

FWIW:在我的testing中,至lessRenderControl()调用不像页面控件,所以我使用UserControl。

我很确定我已经包括了一切。 让我知道,如果我留下了一些东西。

HTH

用法:

 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} <james.bond@mi6.co.uk>".ToFormat(oTemplate.Name), "Purchase Receipt", oTemplate.ToHtml) End Sub End Class 

基类:

 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 

“工厂”类:

 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 

模板类:

 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 

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> 

ASCX页脚:

 <%@ Control Language="VB" ClassName="_Footer" %> </body> </html> 

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模板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 

助手:

 ' ' FindNestedControl helpers based on tip by @andleer ' at http://stackoverflow.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