我能否使用ASP.NET设置HTML/电子邮件模板?

100

我正在开发一个将会发送大量电子邮件的网站。我希望设置页眉和页脚文本,甚至可能使用模板来让用户轻松编辑这些电子邮件。

如果我在C#字符串文字中嵌入HTML代码,这样做很丑,而且他们还需要担心转义的问题。包含头部和尾部内容的纯文本文件可能可行,但总感觉有些不对劲。

理想情况是使用.ASPX页面作为某种模板,然后只需告诉我的代码提供该页面,并使用返回的HTML内容用于电子邮件。

有没有一种简单易行的方法?有没有更好的解决方法?

更新:
我添加了一个答案,使您可以使用标准的.aspx页面作为电子邮件模板。像通常一样替换所有变量,使用数据绑定等。然后只需捕获页面输出,就可以得到HTML电子邮件!

更新并附有警告!!!:
我在一些aspx页面上使用MailDefinition类非常顺利,但在尝试在运行的服务器进程中使用此类时失败了。我认为这是因为MailDefinition.CreateMailMessage()方法需要引用有效的控件,即使它并不总是这样做。因此,我建议使用我的方法来使用aspx页面,或者Mun的方法来使用ascx页面,后者似乎更好。


另一个解决方案是使用AlphaMail来使用C#和Comlang模板语言创建和发送电子邮件。 - Timothy E. Johansson
1
@JohnBubriski:我解决了你在“更新说明”中提到的控件问题,使用new System.Web.UI.Control(),代码如下:mailDefinition.CreateMailMessage("somebody@fake.com", iDictionaryReplacements, new System.Web.UI.Control()) - Theophilus
是的,我也做过那样的事情,但随着 Razor 的出现,这已经不再是一个好主意了。 - John B
24个回答

75

这里已经有很多答案了,但我偶然发现一篇关于如何使用 Razor 进行电子邮件模板化的好文章。Razor 是随着 ASP.NET MVC 3 推出的,但不一定需要 MVC 才能使用 Razor。这是非常巧妙的处理电子邮件模板的方法。

正如文章所述,“Razor 最好的一点是,与其前身 WebForms 不同,它不与 web 环境绑定,我们可以轻松地将其托管在 web 之外,并将其用作各种目的的模板引擎。”

使用 RazorEngine 生成 HTML 电子邮件 - 第 01 部分 - 介绍

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

智能电子邮件模板在 ASP.NET 中的应用

类似的 Stackoverflow Q&A

使用新的 RazorEngine API 进行模板化

在不使用 MVC 的情况下使用 Razor

是否可能在 asp.net 之外使用 Razor 视图引擎


1
+1 但是如果用户提供了模板,则要小心,因为他们可以从模板中运行C#代码,这会在您的系统中赋予他们比您想要的更多的权限。 - AaronLS
你对安全有什么看法?使用这个模板引擎可以可能格式化整个文件系统。我喜欢这个引擎,但这迫使我去看其他引擎。 - der_chirurg

55
你或许可以尝试加载一个控件,然后将其渲染成一个字符串,并将其设置为HTML Body:
// 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();
你可以像平常一样构建你的电子邮件:

你可以像平常一样构建你的电子邮件:

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

你的用户控件可以包含其他控件,例如头部和底部,并且可以利用数据绑定等功能。


9
这个例子没有说明代码存在于哪里,它是在一个页面上吗?因为LoadControl是一个页面/控件的方法。 - Shrage Smilowitz
@ Mun,你将用户控件加载到一个名为ctrl的变量中,但在你的代码中再也没有引用它。这样怎么能正常工作呢? - The Muffin Man
@Nick:你在Page_Load事件中绑定了ListView吗?没有看到代码,很难猜测问题出在哪里,但听起来ListView的绑定方式可能需要一些修改。 - Mun
@ Mun,看一下我之前提出的问题的答案:https://dev59.com/_1fUa4cB1Zd3GeqPDwSU - The Muffin Man
它可以进一步增强。向用户控件添加一个公共方法。将用户控件强制转换为其类名,而不是通用的UserControl。现在我们可以调用这个公共方法来像Page_Load一样对待用户控件,通过在加载用户控件后立即调用它。这有助于轻松地传递参数给用户控件。 - iMatoria
显示剩余5条评论

35

4
我想指出的是,这对于简单的电子邮件是很好的,但对于任何复杂的内容则不行。MailDefinition类不支持数据绑定,它真正做的只是提供字符串替换。尽管如此,它也内置在Membership Account Creation Wizard中。 - John B
4
MailDefinition类必须获取一个控件来渲染模板内容。不太好理解。 MailDefinition类需要一个控件来呈现模板内容。 - Yuki

17

如果你想传递像用户名、产品名称等参数,你可以使用开源模板引擎 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制作的样本电子邮件模板。

示例:

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

[日期:21.02.2009] 名称:项目4, 值:52

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

[日期:23.03.2009] 名称:项目6, 值:24

如果需要编辑模板,可以使用FCKEditor并将您的模板保存到文件中。


7

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电子邮件组件并尝试使用。
请注意,这是我创建的商业产品。

6
如果您需要灵活性,XSLT可能是一个不错的选择,它完全受到.NET框架的支持,甚至可以让用户编辑这些文件。这篇文章(http://www.aspfree.com/c/a/XML/XSL-Transformations-using-ASP-NET/)可能对入门有所帮助(msdn上有更多信息)。正如ScarletGarden所说,NVelocity也是另一个不错的选择,但我更喜欢XSLT,因为它具有“内置”的.NET框架支持和平台无关性。

我以前从未想过这一点,但在尝试了许多其他方法后,我发现将IXmlSerializable接口添加到我的类中与此相结合效果很好。只需几行代码,我就可以将我的类转换为电子邮件。 - cjbarth
我梦到过XSLT的噩梦。可能是我接触过的最不直观的编程/标记语言。即使是在你第一次编写XSLT一个月之后,对于别人和甚至自己来说都难以维护。 - PussInBoots

5
我认为你也可以这样做:
创建一个.aspx页面,并在OnLoad方法的末尾添加以下内容,或者手动调用它。
    StringBuilder sb = new StringBuilder();
    StringWriter sw = new StringWriter(sb);
    HtmlTextWriter htmlTW = new HtmlTextWriter(sw);
    this.Render(htmlTW);

我不确定这种方法是否存在潜在问题,但看起来它应该可以工作。这样,你就可以使用完整功能的.aspx页面,而不是仅支持文本替换的MailDefinition类。


虽然MailDefinition类是一个不错的起点,但它有点简陋。这个方法应该支持更多的功能,比如数据绑定,甚至可能会追踪。对此有什么想法或潜在的问题吗? - John B
太好了!你有遇到任何问题吗? - John B
那么当用户需要更改邮件模板时,您会让他们编辑.aspx文件吗?我认为这可能会成为一个问题。 - Bryan
我不这么认为,至少风险不比他们可以编辑的其他模板更高。当然,如果他们知道他们在做什么,他们可能会造成伤害,但在这种情况下,这种可能性很小。它不会是一个复杂的.aspx页面,更像是一个带有占位符的模板。 - John B
已经有一段时间了,我知道,但你还记得你的最终解决方案吗?当我使用通用扩展方法进行渲染时,我无法让这种特定方法与“Page”一起工作。因此,我转而使用“UserControl”;请参见下面的答案。到目前为止,它似乎运行良好... 我很想了解你当时是如何解决它的。 - InteXX
点赞这个答案是因为你特别回答了如何在.aspx页面中实现它,这可能会帮助其他使用这些模板的人。 - ahong

4

你当然可以创建一个HTML模板,我建议同时创建一个文本模板。在模板中,你只需将 [BODY] 放置为正文所在的位置,然后读取模板并用新内容替换正文部分。你可以使用 .Net 的 Mail 类来发送电子邮件。在初始创建电子邮件后,你只需循环发送邮件给所有收件人即可。对我而言,这种方法非常有效。

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

你可以剪掉StreamReader的部分,然后用File.ReadAllText(path)替换。 - John Sheehan
这是一个不错的开始,但只提供页眉和页脚的功能。这并不能真正帮助处理正文部分。 - John B
在编写正文时,您只需将所需的正文内容输入到HTMLBody和TextBody字段中,或者当然也可以将它们存储在文件中。 - Josh Mein

4

2
喜欢这个链接。谢谢!我的大脑开始思考,意识到我可以再进一步,有一个XSLT模板,可以将XML可序列化对象或WCF数据合同直接转换为HTML电子邮件格式。突然间,我通过实际的可序列化类拥有了“强类型”电子邮件模板! - CodingWithSpike

2

在进行此操作时要小心,SPAM过滤器似乎会阻止ASP.net生成的HTML,显然是因为ViewState的原因,因此如果您要执行此操作,请确保生成的Html干净。

个人建议使用Asp.net MVC来实现所需的结果。或者NVelocity也很擅长这方面。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接