簡體   English   中英

我可以使用 ASP.NET 設置 HTML/電子郵件模板嗎?

[英]Can I set up HTML/Email Templates with ASP.NET?

我正在開發一個會發送大量電子郵件的網站。 我想設置 header 和頁腳文本,或者甚至是模板,以允許用戶在需要時輕松編輯這些電子郵件。

如果我將 HTML 嵌入到 C# 字符串文字中,這很丑陋,他們將不得不擔心 escaping。 包括 header 和頁腳的平面文件可能會起作用,但它的某些內容感覺不對。

以某種方式使用.ASPX頁面作為模板,然后只告訴我的代碼為該頁面提供服務,並使用為 email 返回的 HTML 將是理想的。

有沒有一種簡單易用的方法來做到這一點? go 有沒有更好的方法解決這個問題?

更新:
我添加了一個答案,使您能夠使用 standard.aspx 頁面作為 email 模板。 只需像往常一樣替換所有變量,使用數據綁定等。然后只需捕獲頁面的 output,瞧! 您有您的 HTML 電子郵件!

更新警告:!!:
我在某些 aspx 頁面上使用 MailDefinition class 就好了,但是當嘗試在正在運行的服務器進程期間使用這個 class 時,它失敗了。 我相信這是因為 MailDefinition.CreateMailMessage() 方法需要一個有效的控件來引用,即使它並不總是做某事。 正因為如此,我會推薦我的方法使用 aspx 頁面,或者 Mun 的方法使用 ascx 頁面,這似乎更好一些。

這里已經有很多答案,但我偶然發現了一篇關於如何將 Razor 與 email 模板一起使用的好文章。 Razor 是使用 ASP.NET MVC 3 推送的,但使用 Razor 不需要 MVC。 這是做 email 模板的非常巧妙的處理

正如文章指出的那樣,“Razor 的最佳之處在於,與它的前身(webforms)不同,它不與 web 環境綁定,我們可以輕松地將它托管在 web 環境之外,並將其用作各種目的的模板。”

使用 RazorEngine 生成 HTML 電子郵件 - 第 01 部分 - 簡介

利用 ASP.NET 之外的 Razor 模板:它們不再僅適用於 HTML!

帶有 RazorEngine 的 ASP.NET 中的更智能的 email 模板

類似的 Stackoverflow 質量檢查

使用新的 RazorEngine API 進行模板化

使用不帶 MVC 的 Razor

是否可以在 asp.net 之外使用 Razor 查看引擎

您可能還想嘗試加載控件,然后將其呈現為字符串並將其設置為 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();

然后你可以像往常一樣構造你的 email :

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

您的用戶控件可以包含其他控件,例如 header 和頁腳,並且還可以利用數據綁定等功能。

你可以試試MailDefinition class

如果您想傳遞用戶名、產品名稱等參數,您可以使用開源模板引擎NVelocity來生成最終的 email / 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 的示例email模板。

Foreach 示例:

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

[日期:21.02.2009] 名稱:項目 4,價值:52

[日期:01.03.2009] 名稱:項目 2,值:21

[日期:23.03.2009] 名稱:項目 6,值:24

對於編輯模板,也許您可以使用FCKEditor並將您的模板保存到文件中。

Mail.dll email 組件包括 email 模板引擎:

這是語法概述:

<html>
<body>
Hi {FirstName} {LastName},

Here are your orders: 
{foreach Orders}
    Order '{Name}' sent to <strong>{Street}</strong>. 
{end}

</body>
</html>

加載模板的代碼從 c# object 填充數據並發送 email:

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

您可以在email 模板引擎博客文章中獲得更多信息。

或者只需下載Mail.dll email 組件並試一試。

請注意,這是我創建的商業產品。

如果靈活性是您的先決條件之一,XSLT 可能是一個不錯的選擇,它完全由 .NET 框架支持,您甚至可以讓用戶編輯這些文件。 這篇文章( http://www.aspfree.com/c/a/XML/XSL-Transformations-using-ASP-NET/ )可能對開始很有用(msdn 有更多關於它的信息)。 正如 ScarletGarden 所說,NVelocity 是另一個不錯的選擇,但我更喜歡 XSLT,因為它的“內置”.NET 框架支持和平台無關。

我想你也可以這樣做:

創建and.aspx頁面,並將其放在OnLoad方法的末尾,或者手動調用。

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

我不確定這是否有任何潛在問題,但看起來它會起作用。 這樣,您可以使用全功能的.aspx 頁面,而不是僅支持文本替換的 MailDefinition class。

這是另一種使用 XSL 轉換來處理更復雜的 email 模板的替代方法: 從 .NET 應用程序發送基於 HTML 的 email

當然你可以創建一個 html 模板,我也推薦一個文本模板。 在模板中,您可以將 [BODY] 放在要放置正文的位置,然后您可以閱讀模板並將正文替換為新內容。 您可以使用.Nets Mail Class 發送 email。 在您最初創建 email 后,您只需循環發送 email 給所有收件人。 對我來說就像一個魅力。

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

這樣做時要小心,SPAM 過濾器似乎會阻止 ASP.net 生成的 html,顯然是因為 ViewState,所以如果你要這樣做,請確保生成的 Html 是干凈的。

我個人會考慮使用 Asp.net MVC 來實現您想要的結果。 或者NVelocity很擅長這個

DotLiquid 是另一種選擇。 您將 class model 中的值指定為{{ user.name }} ,然后在運行時提供該 class 中的數據,並將模板中的值與標記合並。 它在很多方面類似於使用 Razor 模板引擎。 它支持更復雜的東西,比如循環和各種 function,比如 ToUpper。 好消息是這些是“安全的”,因此創建模板的用戶不會像在 razor 中那樣使您的系統崩潰或編寫不安全的代碼: http://dotliquidmarkup.org/try-online

請注意,aspx 和 ascx 解決方案需要當前的 HttpContext,因此不能在沒有大量工作的情況下異步使用(例如在線程中)。

我認為簡單的答案是 MvcMailer。 它是 NuGet package,可讓您使用自己喜歡的視圖引擎生成電子郵件。 請參閱此處的 NuGet package 和項目文檔

希望能幫助到你!

以某種方式使用 a.ASPX 頁面作為模板,然后告訴我的代碼為該頁面提供服務,並使用為 email 返回的 HTML 將是理想的。

您可以輕松地構造一個 WebRequest 來訪問 ASPX 頁面並獲得結果 HTML。 再做一些工作,您可能無需 WebRequest 就可以完成它。 PageParser 和 Response.Filter 將允許您運行頁面並捕獲 output ......盡管可能有一些更優雅的方式。

我對其中一個項目有類似的要求,您必須每天發送大量電子郵件,而客戶希望完全控制 html 模板以用於不同類型的電子郵件。

由於要發送大量電子郵件,因此性能是主要問題。

我們想出的是 sql 服務器中的 static 內容,您可以在其中保存整個 html 模板標記(以及占位符,例如 [UserFirstName],[UserLastName] 不同類型的電子郵件替換為運行時數據)

然后我們將這些數據加載到 asp.net 緩存中 - 所以我們不會一遍又一遍地讀取 html 模板 - 但只有在它們實際更改時

我們為客戶提供了一個所見即所得的編輯器,以通過管理員 web 表單修改這些模板。 每當進行更新時,我們都會重置 asp.net 緩存。

然后我們有一個單獨的表用於 email 日志 - 記錄每個要發送的 email 。 該表有名為 emailType、emailSent 和 numberOfTries 的字段。

對於需要盡快發送的重要 email 類型(如新會員注冊、忘記密碼),我們只需每 5 分鍾運行一次作業

我們每 15 分鍾為不太重要的 email 類型運行另一項工作(如促銷 email、新聞 email 等)

這樣您就不會阻止您的服務器發送不間斷的電子郵件,並且您可以批量處理郵件。 發送 email 后,將 emailSent 字段設置為 1。

這是使用WebClient class 的簡單方法:

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 需要內聯才能在大多數 email 客戶端(例如 Outlook)中顯示網頁的 styles。 如果您的電子郵件顯示動態內容(例如客戶姓名),那么我建議在您的網站上使用 QueryStrings 來填充數據。 (例如http://www.yourwebsite.com?CustomerName=Bob

類似於 Canavar 的答案,但不是 NVelocity,而是我總是使用“ StringTemplate ”,我從配置文件加載模板,或者使用 File.ReadAllText() 加載外部文件並設置值。

這是一個 Java 項目,但 C# 端口是可靠的,我已經在幾個項目中使用過它(僅用於 email 模板文件中的模板)。

替代品總是好的。

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

我在 VS 2013 中在網站中使用 WebForms(我發誓我永遠不會再使用網站——多么 PITA)。

我嘗試了 Razor 的建議,但我作為一個網站我沒有得到 IDE 在 MVC 項目中提供的最重要的 IntelliSense。 我還喜歡在我的模板中使用設計器——這是 UserControl 的完美位置。

再次在 Razor 上使用 Nix。

所以我想出了這個小框架(@mun 用於 UserControl,@imatoria 用於強類型)。 我能看到的唯一潛在的麻煩點是你必須小心保持你的 .ASCX 文件名與其 class 名稱同步。 如果你迷路了,你會得到一個運行時錯誤。

FWIW:在我的測試中,至少 RenderControl() 調用不喜歡 Page 控件,所以我選擇了 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

基礎 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

“工廠”Class:

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

模板 Class:

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 Header:

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

只需將我正在使用的庫放入混合中: https://github.com/lukencode/FluentEmail

它使用RazorLight渲染電子郵件,使用 fluent 樣式構建電子郵件,並支持開箱即用的多個發件人。 它還帶有 ASP.NET DI 的擴展方法。 使用簡單,設置少,具有純文本和 HTML 支持。

如果您能夠允許 ASPNET 和相關用戶讀取和寫入文件的權限,您可以輕松地使用帶有標准String.Format()占位符( {0}{1:C}等)的 HTML 文件來完成這個。

僅使用System.IO命名空間中的類以字符串形式讀取文件。 獲得該字符串后,將其作為第一個參數傳遞給String.Format() ,並提供參數。

保留該字符串,並將其用作電子郵件的正文,您基本上就完成了。 我們今天在幾十個(誠然很小的)網站上執行此操作,並且沒有遇到任何問題。

我應該注意,如果(a)您不一次發送無數電子郵件,(b)您不個性化每封電子郵件(否則您會吃掉大量字符串)和(c ) HTML 文件本身比較小。

設置設置 Email Message IsBodyHtml = true

獲取包含 email 內容的 object 序列化 object 並使用 xml/xslt 生成 ZFC35FZ70D5FC67A5E326 內容。

如果你想做 AlternateViews 做同樣的事情 jmein 只使用不同的 xslt 模板來創建純文本內容。

這樣做的主要優點之一是如果您想更改布局,您只需更新 xslt 模板。

看看 SubSonic (www.subsonicproject.com)。 他們這樣做是為了生成代碼——模板是標准的 ASPX,它輸出 c#。 對於您的方案,相同的方法可以重復使用。

我會使用像TemplateMachine這樣的模板庫。 這允許您將 email 模板與普通文本放在一起,然后根據需要使用規則注入/替換值。 與 Ruby 中的 ERB 非常相似。 這使您可以將郵件內容的生成分開,而不會將您與 ASPX 等內容過於緊密地聯系在一起。然后一旦生成了內容,您就可以離開 email。

我喜歡拉傑的回答。 Programs like ListManager & frameworks like DNN do similar things, and if easy editing by non-technical users is required, WYSIWYG editors to modify HTML stored in SQL is a mostly easy, straightforward way to go and can easily accommodate editing headers independently from footers,等,以及使用令牌動態插入值。

如果使用上述方法(或任何方法,真的),要記住的一件事是嚴格和小心您允許編輯器插入哪些類型的樣式和標簽。 如果您認為瀏覽器很挑剔,請等到您看到 email 客戶端呈現相同內容的方式有何不同...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM