问题描述

有没有更好的方法来生成 C#中的 HTML 电子邮件 (通过 System.Net.Mail 发送),而不是使用 Stringbuilder 来执行以下操作:

string userName = "John Doe";
StringBuilder mailBody = new StringBuilder();
mailBody.AppendFormat("<h1>Heading Here</h1>");
mailBody.AppendFormat("Dear {0}," userName);
mailBody.AppendFormat("<br />");
mailBody.AppendFormat("<p>First part of the email body goes here</p>");

等等等等?

最佳解决方案

您可以使用 MailDefinition class

这是你如何使用它:

MailDefinition md = new MailDefinition();
md.From = "test@domain.com";
md.IsBodyHtml = true;
md.Subject = "Test of MailDefinition";

ListDictionary replacements = new ListDictionary();
replacements.Add("{name}", "Martin");
replacements.Add("{country}", "Denmark");

string body = "<div>Hello {name} You're from {country}.</div>";

MailMessage msg = md.CreateMailMessage("you@anywhere.com", replacements, body, new System.Web.UI.Control());

另外,我写了一篇关于如何 generate HTML e-mail body in C# using templates using the MailDefinition class 的博文。

次佳解决方案

使用 System.Web.UI.HtmlTextWriter 类。

StringWriter writer = new StringWriter();
HtmlTextWriter html = new HtmlTextWriter(writer);

html.RenderBeginTag(HtmlTextWriterTag.H1);
html.WriteEncodedText("Heading Here");
html.RenderEndTag();
html.WriteEncodedText(String.Format("Dear {0}", userName));
html.WriteBreak();
html.RenderBeginTag(HtmlTextWriterTag.P);
html.WriteEncodedText("First part of the email body goes here");
html.RenderEndTag();
html.Flush();

string htmlString = writer.ToString();

对于包含创建样式属性的广泛 HTML,HtmlTextWriter 可能是最好的方式。但是它可能有点笨重的使用和一些开发人员喜欢的标记本身是容易阅读,但颠覆性的 HtmlTextWriter 的选择与缩进是有点 wierd 。

在这个例子中,您还可以非常有效地使用 XmlTextWriter:

writer = new StringWriter();
XmlTextWriter xml = new XmlTextWriter(writer);
xml.Formatting = Formatting.Indented;
xml.WriteElementString("h1", "Heading Here");
xml.WriteString(String.Format("Dear {0}", userName));
xml.WriteStartElement("br");
xml.WriteEndElement();
xml.WriteElementString("p", "First part of the email body goes here");
xml.Flush();

第三种解决方案

更新答案:

本答案中使用的 SmtpClient 文档现在读为 「Obsolete(」SmtpClient 及其网络类型设计不佳,我们强烈建议您使用 https://github.com/jstedfast/MailKithttps://github.com/jstedfast/MimeKit「) 。

资料来源:https://www.infoq.com/news/2017/04/MailKit-MimeKit-Official

原始答案:

使用 MailDefinition 类是错误的方法。是的,它很方便,但它也是原始的,并且取决于 Web UI 控件 – 这对于通常是 server-side 任务的东西没有意义。

下面介绍的方法是基于 MSDN 文档和 Qureshi’s post on CodeProject.com

注意:此示例从嵌入式资源中提取 HTML 文件,图像和附件,但使用其他替代方法获取这些元素的流即可。 hard-coded 字符串,本地文件等。

Stream htmlStream = null;
Stream imageStream = null;
Stream fileStream = null;
try
{
    // Create the message.
    var from = new MailAddress(FROM_EMAIL, FROM_NAME);
    var to = new MailAddress(TO_EMAIL, TO_NAME);
    var msg = new MailMessage(from, to);
    msg.Subject = SUBJECT;
    msg.SubjectEncoding = Encoding.UTF8;
 
    // Get the HTML from an embedded resource.
    var assembly = Assembly.GetExecutingAssembly();
    htmlStream = assembly.GetManifestResourceStream(HTML_RESOURCE_PATH);
 
    // Perform replacements on the HTML file (if you're using it as a template).
    var reader = new StreamReader(htmlStream);
    var body = reader
        .ReadToEnd()
        .Replace("%TEMPLATE_TOKEN1%", TOKEN1_VALUE)
        .Replace("%TEMPLATE_TOKEN2%", TOKEN2_VALUE); // and so on...
 
    // Create an alternate view and add it to the email.
    var altView = AlternateView.CreateAlternateViewFromString(body, null, MediaTypeNames.Text.Html);
    msg.AlternateViews.Add(altView);
 
    // Get the image from an embedded resource. The <img> tag in the HTML is:
    //     <img src="pid:IMAGE.PNG">
    imageStream = assembly.GetManifestResourceStream(IMAGE_RESOURCE_PATH);
    var linkedImage = new LinkedResource(imageStream, "image/png");
    linkedImage.ContentId = "IMAGE.PNG";
    altView.LinkedResources.Add(linkedImage);
 
    // Get the attachment from an embedded resource.
    fileStream = assembly.GetManifestResourceStream(FILE_RESOURCE_PATH);
    var file = new Attachment(fileStream, MediaTypeNames.Application.Pdf);
    file.Name = "FILE.PDF";
    msg.Attachments.Add(file);
 
    // Send the email
    var client = new SmtpClient(...);
    client.Credentials = new NetworkCredential(...);
    client.Send(msg);
}
finally
{
    if (fileStream != null) fileStream.Dispose();
    if (imageStream != null) imageStream.Dispose();
    if (htmlStream != null) htmlStream.Dispose();
}

第四种方案

我会推荐使用某种模板。有各种不同的方法可以解决这个问题,但实质上是持有 Email 的一些模板 (在磁盘上,数据库中等等),只需将密钥数据 (IE:收件人名称等) 插入到模板中即可。

这更灵活,因为这意味着您可以根据需要更改模板,而无需更改代码。根据我的经验,您可能会收到最终用户更改模板的请求。如果你想去整个猪,你可以包括一个模板编辑器。

第五种方案

只要标记不是太复杂,发出像这样的手工制作的 HTML 可能是最好的方式。字符串制造商只有在大约三个连接之后才开始付出代价,所以对于真正简单的字符串+字符串将做的事情。

除此之外,您可以开始使用 html 控件 (System.Web.UI.HtmlControls) 并渲染它们,这样您有时可以继承它们,并为复杂的条件布局制定自己的契约。

第六种方案

我使用 dotLiquid 完成这项任务。

它需要一个模板,并使用匿名对象的内容填充特殊标识符。

//define template
String templateSource = "<h1>{{Heading}}</h1>Dear {{UserName}},<br/><p>First part of the email body goes here");
Template bodyTemplate = Template.Parse(templateSource); // Parses and compiles the template source

//Create DTO for the renderer
var bodyDto = new {
    Heading = "Heading Here",
    UserName = userName
};
String bodyText = bodyTemplate.Render(Hash.FromAnonymousObject(bodyDto));

它也适用于集合,请参阅 some online examples

参考文献

注:本文内容整合自 Google/Baidu/Bing 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛