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


97

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

如果我将HTML嵌入到C#字符串文字中,那么这很丑陋,他们将不得不担心转义。包括用于页眉和页脚的平面文件可能会起作用,但是关于它的某些感觉并不对。

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

有没有一种简单方便的方法可以做到这一点?有没有更好的方法来解决这个问题?

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


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


另一个解决方案是使用AlphaMail使用C#和Comlang模板语言创建和发送电子邮件。
Timothy E. Johansson

1
@JohnBubriski:我的工作围绕控制问题,你提到“更新了CAVEAT”使用new System.Web.UI.Control()为:mailDefinition.CreateMailMessage("somebody@fake.com", iDictionaryReplacements, new System.Web.UI.Control())
Theophilus 2014年

是的,我也这样做了,但是随着Razor的出现,这已经不是一个好主意了。
约翰Bubriski

Answers:


73

这里已经有很多答案,但是我偶然发现了一篇很棒的文章,关于如何将Razor与电子邮件模板一起使用。Razor已随ASP.NET MVC 3一起推出,但使用Razor不需要MVC。这是处理电子邮件模板的非常巧妙的处理

正如文章所指出的那样,“ Razor的最好之处在于,与它的前身(webforms)不同,它不与Web环境捆绑在一起,我们可以轻松地将其托管在Web之外,并用作各种目的的模板引擎。”

使用RazorEngine生成HTML电子邮件-第1部分-简介

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

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

类似的Stackoverflow质量检查

使用新的RazorEngine API进行模板

在没有MVC的情况下使用Razor

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


1
+1,但要小心,如果用户正在提供模板,因为他们可以从模板运行C#代码,从而使它们在系统中的功能比您可能想要的要多。
AaronLS 2013年

您如何看待安全性。使用此模板引擎可以格式化整个文件系统。我喜欢引擎,但这迫使我去看看其他引擎。
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);

您的用户控件可以包含其他控件(例如,页眉和页脚),也可以利用数据绑定等功能。


我第一次以某种方式错过了这个答案。与我的解决方案类似,但是使用ascx而不是aspx。我仍然认为aspx会更好,因为它将提供完整的页面而不是控件,但这只是我的想法。
约翰·布布里斯基

是的,您可以使用任何一种解决方案...它们的工作方式相同。这种方法的一个好处是一致性。例如,您可以向用户显示订单摘要,并通过重复使用相同的控件在确认电子邮件中包含完全相同的内容。

次要点,但是您缺少在第一个代码块中声明StringBuilder的行。
Kirschstein,2009年

9
该示例没有解释代码所在的位置,而是页面吗,因为LoadControl是页面/控件方法。
Shrage Smilowi​​tz 2010年

@Mun,您将用户控件加载到名为ctrl的变量中,并且再也不会在代码中再次引用它了。这应该如何工作?
松饼人

35

您可以尝试MailDefinition类


4
我只想指出,这对基本电子邮件很有用,但并不复杂。MailDefinition类不支持数据绑定。它唯一真正要做的就是提供字符串替换。虽然,它还内置在“会员帐户创建向导”中。
John Bubriski

4
MailDefinition类必须获得一个Control才能呈现模板化的内容。
由纪

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的示例电子邮件模板 。

Foreach示例:

[日期:2009年2月12日]名称:项目1,值:09

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

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

[日期:2009年3月23日]名称:第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框架完全支持XSLT,您甚至可以让用户编辑这些文件。本文(http://www.aspfree.com/c/a/XML/XSL-Transformations-using-ASP-NET/)可能对开始有用(msdn对此有更多信息)。正如ScarletGarden所说,NVelocity是另一个不错的选择,但我确实更喜欢XSLT,因为它具有“内置” .NET框架,并且与平台无关。


我以前从没想过,但是尝试了许多其他方法后,我发现这与将IXmlSerializable接口添加到类中结合起来效果很好。只需几行,我就可以将我的课程上交电子邮件。
cjbarth 2013年

Urgh,我梦到了关于XSLT的噩梦。可能是我使用过的最不直观的编程/标记语言。在您对XSLT进行首次编码后的1个月内,就不可能为他人甚至自己维护。
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 Bubriski

大!你有什么问题吗?
John Bubriski

因此,当用户需要更改邮件模板时,将让他们编辑.aspx文件吗?我认为这是一个潜在的问题。
布赖恩2009年

我认为,至少,他们可以编辑的其他模板没有更多的风险。当然,如果他们知道自己在做什么,就可能造成伤害,但至少在这种情况下,伤害的可能性很小。它不会是一个复杂的.aspx页面,更多的是带有占位符的模板。
John Bubriski

我知道已经过去了一段时间,但是您还记得您的最终解决方案吗?Page至少在使用通用扩展方法进行渲染时,我无法使用这种特殊方法来处理。因此我转向UserControl; 请参阅下面的答案。到目前为止,它似乎运行良好。我很想了解您当时的工作情况。
InteXX 2014年

4

当然,您可以创建一个html模板,我也建议您使用一个文本模板。在模板中,您可以将[BODY]放在放置正文的位置,然后可以阅读模板并用新内容替换正文。您可以使用.Nets邮件类发送电子邮件。最初创建电子邮件后,您只需要遍历向所有收件人发送电子邮件的过程。对我来说就像一个魅力。

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 Bubriski

正文,您所要做的就是在HTMLBody和TextBody字段中输入所需的正文内容,或者您​​当然也可以将它们存储在文件中
Josh Mein 2009年

4

这是将XSL转换用于更复杂的电子邮件模板的另一种选择:从.NET应用程序发送基于HTML的电子邮件


2
喜欢的链接。谢谢!我的大脑开始转动,意识到我可以进一步迈出一步,有了一个XSLT模板,该模板可以将XML Serializable对象或WCF数据协定直接转换为html-email格式。突然之间,我将通过实际的可序列化类获得“强类型”电子邮件模板!
CodingWithSpike 2011年

2

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

我个人会考虑使用Asp.net MVC来达到您想要的结果。或NVelocity在这方面很擅长


1

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

您可以轻松地构造一个WebRequest来访问ASPX页面并获取结果HTML。通过更多的工作,您可能不需要WebRequest就可以完成它。PageParser和Response.Filter可以让您运行页面并捕获输出...尽管可能有一些更优雅的方法。


1

我对其中一个项目有类似的要求,在该项目中,您每天必须发送大量电子邮件,并且客户希望完全控制不同类型电子邮件的html模板。

由于要发送大量电子邮件,因此性能是首要考虑因素。

我们想出的是sql服务器中的静态内容,您在其中保存了整个html模板标记(以及占位符,例如[UserFirstName],[UserLastName]等占位符,它们在运行时已替换为实际数据),用于不同类型的电子邮件

然后我们将此数据加载到asp.net缓存中-因此我们不会一遍又一遍地读取html模板-但仅当它们实际更改时

我们为客户提供了一个所见即所得的编辑器,可以通过管理Web表单修改这些模板。每当进行更新时,我们都会重置asp.net缓存。

然后我们有一个单独的表来记录电子邮件日志-每个要发送的电子邮件都记录在其中。该表具有名为emailType,emailSent和numberOfTries的字段。

我们只需每5分钟就需要发送的重要电子邮件类型(例如新会员注册,忘记密码)运行一份工作

我们每15分钟针对不太重要的电子邮件类型(例如促销电子邮件,新闻电子邮件等)运行另一项工作

这样,您就不会阻止服务器发送不间断电子邮件,并且可以批量处理邮件。发送电子邮件后,将emailSent字段设置为1。


但是您如何处理收藏呢?
Riri

1
我也这样做了,效果很好。另外,如果您需要举报,那么您可以从历史上回顾过去并查看已发送电子邮件的记录。
Mark Glorie

1

请注意,aspx和ascx解决方案需要当前的HttpContext,因此,如果不进行大量工作就无法异步使用(例如在线程中)。


1

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

希望能帮助到你!


嗯,奇怪的是,这个答案没有引起足够的重视?
PussInBoots

1

DotLiquid是另一种选择。您可以从类模型中指定值{{ user.name }},然后在运行时在该类中提供数据以及带有标记的模板,它将为您合并这些值。它在许多方面类似于使用Razor模板引擎。它支持更复杂的事物(例如循环)和各种功能(例如ToUpper)。不错的是,这些都是“安全的”,因此创建模板的用户不会像剃刀一样崩溃系统或编写不安全的代码:http : //dotliquidmarkup.org/try-online


0

如果你能允许ASPNET和关联用户的权限读写文件,你可以方便地使用标准的HTML文件String.Format()占位符({0}{1:C},等)来实现这一目标。

仅使用System.IO名称空间中的类将文件作为字符串读入。有了该字符串后,请将其作为第一个参数传递给String.Format(),并提供参数。

保留该字符串,并将其用作电子邮件的正文,您基本上已经完成了。今天,我们在数十个(公认的很小的)站点上执行此操作,并且没有任何问题。

我应该注意,如果(a)您一次不会发送大量电子邮件,(b)您没有个性化每封电子邮件(否则您会吃掉很多字符串),并且(c )HTML文件本身相对较小。


0

设置设置电子邮件IsBodyHtml = true

取出包含电子邮件内容的对象序列化对象,然后使用xml / xslt生成html内容。

如果要执行AlternateView,则做的事情与jmein相同,仅使用不同的xslt模板来创建纯文本内容。

这样做的主要优点之一是,如果要更改布局,则只需更新xslt模板即可。


0

查看SubSonic(www.subsonicproject.com)。他们正是这样做以生成代码-模板是标准ASPX,并输出c#。对于您的方案,可以使用相同的方法。


0

我将使用诸如TemplateMachine的模板库。这样一来,您通常可以将电子邮件模板与普通文本放在一起,然后根据需要使用规则插入/替换值。与Ruby中的ERB非常相似。这样一来,您就可以分离出邮件内容的生成,而不必过于依赖ASPX等东西。一旦生成了邮件内容,就可以通过电子邮件发送邮件了。


0

我喜欢拉杰的回答。诸如ListManager之类的程序和诸如DNN之类的框架也可以做类似的事情,并且如果需要非技术用户轻松地进行编辑,则所见即所得的编辑器修改存储在SQL中的HTML是最简单,直接的方法,并且可以轻松地独立于页脚而容纳编辑标头,等,以及使用令牌动态插入值。

如果使用上述方法(或实际上使用任何方法),要记住的一件事是严格和谨慎地允许编辑者插入哪种类型的样式和标签。如果您认为浏览器有点挑剔,请等到看到电子邮件客户端呈现相同内容的方式有所不同时...


0

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

这是一个Java项目,但是C#端口是可靠的,我已经在多个项目中使用了它(只是使用它在外部文件中使用模板来进行电子邮件模板处理)。

替代方案总是好的。


0

这是使用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);

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


整洁,尽管我认为大多数其他答案都可以在不向站点发出Web请求的情况下完成,即必须在您的站点上托管电子邮件正文。
拉普

@Rup可以理解,但是请记住,很多时候人们还是希望看到电子邮件的“ Web版本”。该解决方案非常适合那种情况。
ROFLwTIME 2012年

0

@bardev提供了一个很好的解决方案,但是不幸的是,它并非在所有情况下都是理想的。我的就是其中之一。

我在VS 2013中在网站中使用WebForms(我发誓我永远不会再使用网站-PITA)。

我尝试了Razor的建议,但是作为一个网站,我没有得到IDE在MVC项目中提供的最重要的IntelliSense。我还喜欢将设计器用于模板,这是UserControl的理想之地。

尼克斯再次剃刀。

所以我想出了这个小框架(对于UserControl,@ mun表示技巧,对于强类型,@ imatoria表示技巧)。我几乎可以看到的唯一潜在问题是,您必须注意保持.ASCX文件名与其类名同步。如果您迷路了,则会遇到运行时错误。

FWIW:在我的测试中,至少RenderControl()调用不喜欢Page控件,因此我使用了UserControl。

我很确定我已经将所有内容都包括在内了;让我知道是否遗漏了一些东西。

高温超导

用法:

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模板代码文件:

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

0

只是将我正在使用的库扔进去:https : //github.com/lukencode/FluentEmail

它使用RazorLight呈现电子邮件,使用流利的样式来构建电子邮件,并支持开箱即用的多个发件人。它也带有ASP.NET DI的扩展方法。使用简单,设置很少,具有纯文本和HTML支持。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.