私はかなりの数のメールを送信するサイトに取り組んでいます。ユーザーが必要に応じてこれらのメールを簡単に編集できるように、ヘッダーテキストとフッターテキストの両方、またはテンプレートを設定したいです。
HTMLをC#文字列リテラル内に埋め込むと、見苦しくなり、エスケープを心配する必要があります。ヘッダーとフッターにフラットファイルを含めることは機能するかもしれませんが、それについて何かが正しく感じられません。
どういうわけか.ASPX
ページをテンプレートとして使用し、そのコードをそのページに配信するように伝え、メールに返されたHTMLを使用することが理想的です。
これを行うための素敵で簡単な方法はありますか?この問題を解決するためのより良い方法はありますか?
更新:
標準の.aspxページをメールテンプレートとして使用できるようにする回答を追加しました。通常どおりすべての変数を置き換え、データバインディングなどを使用します。その後、ページの出力をキャプチャして、出来上がります。 HTMLメールがあります!
警告付きで更新!!!:
一部のaspxページでMailDefinitionクラスを使用していましたが、実行中のサーバープロセス中にこのクラスを使用しようとして失敗しました。 MailDefinition.CreateMailMessage()メソッドは、常に何かをするわけではありませんが、参照するには有効なコントロールが必要だからだと思います。このため、aspxページを使用する私のアプローチ、またはascxページを使用するMunのアプローチをお勧めします。
ここにはたくさんの答えがありますが、私はRazorを電子メールテンプレートで使用する方法についてのすばらしい記事を見つけました。 RazorはASP.NET MVC 3でプッシュされましたが、MVCはRazorを使用するために必須ではありません。これは、メールテンプレートを実行する非常に洗練された処理です。
記事が特定するように、「Razorの最大の利点は、その前身(webforms)とは異なり、Web環境と結び付けられていないため、Webの外部で簡単にホストし、さまざまな目的のテンプレートエンジンとして使用できることです。」
RazorEngineを使用したHTMLメールの生成-パート01-はじめに
ASP.NET外でのRazorテンプレートの活用:HTMLだけではありません!
RazorEngineを使用したASP.NETのスマートな電子メールテンプレート
同様のStackoverflow QA
また、コントロールをロードし、それを文字列にレンダリングして、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("[email protected]", "from name");
message.Subject = "Email Subject";
message.Body = body;
message.BodyEncoding = Encoding.ASCII;
message.IsBodyHtml = true;
SmtpClient smtp = new SmtpClient("server");
smtp.Send(message);
ユーザーコントロールには、ヘッダーやフッターなどの他のコントロールを含めることができ、データバインディングなどの機能も利用できます。
MailDefinition class を試すことができます
ユーザー名、製品名などのパラメータを渡したい場合は、オープンソースのテンプレートエンジン 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の例:
[日付:12.02.2009]名前:アイテム1、値:09
[日付:21.02.2009]名前:アイテム4、値:52
[日付:2009年3月1日]名前:アイテム2、値:21
[日付: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("[email protected]", "Alice"))
.To(new MailBox("[email protected]", "Bob"))
.Subject("Your order")
.UsingNewSmtp()
.WithCredentials("[email protected]", "password")
.Server("mail.com")
.WithSSL()
.Send();
メールテンプレートエンジン ブログ投稿で詳細情報を入手できます。
または、単に Mail.dll電子メールコンポーネント をダウンロードして、試してみてください。
これは私が作成した商用製品であることに注意してください。
柔軟性が前提条件の1つである場合、XSLTが適切な選択である可能性があります。XSLTは.NETフレームワークで完全にサポートされており、ユーザーがそれらのファイルを編集できるようにすることもできます。この記事( http://www.aspfree.com/c/a/XML/XSL-Transformations-using-ASP-NET/ )は、開始に役立つ場合があります(msdnに詳細があります) )。 ScarletGarden NVelocityによると別の良い選択ですが、「組み込み」の.NETフレームワークのサポートとプラットフォームに依存しないXSLTを好みます。
次のようなこともできると思います。
.aspxページを作成し、これをOnLoadメソッドの最後に配置するか、手動で呼び出します。
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
HtmlTextWriter htmlTW = new HtmlTextWriter(sw);
this.Render(htmlTW);
これに潜在的な問題があるかどうかはわかりませんが、うまくいくようです。この方法では、テキストの置換のみをサポートするMailDefinitionクラスの代わりに、フル機能の.aspxページを使用できます。
より複雑な電子メールテンプレートにXSL変換を使用するもう1つの代替方法を次に示します。 。NETアプリケーションからのHTMLベースの電子メールの送信 。
確かにhtmlテンプレートを作成でき、テキストテンプレートもお勧めします。テンプレートでは、本文を配置する場所に[BODY]を配置するだけで、テンプレートを読み込んで本文を新しいコンテンツに置き換えることができます。 .Nets Mail Classを使用してメールを送信できます。最初にメールを作成した後、すべての受信者へのメールの送信をループするだけです。私にとって魅力のように働いた。
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("[email protected]", "display name");
m.To.Add("[email protected]");
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);
これを行う際には、ViewStateのせいで、スパムフィルターがASP.netで生成されたhtmlをブロックするように注意してください。
個人的には、Asp.net MVCを使用して目的の結果を達成することを検討します。または NVelocity はこれでかなり良い
毎日膨大な数のメールを送信する必要があるプロジェクトの1つで同様の要件があり、クライアントはさまざまな種類のメールのhtmlテンプレートを完全に制御することを望んでいました。
送信される電子メールの数が多いため、パフォーマンスが主な関心事でした。
私たちが思いついたのは、さまざまなタイプの電子メール用のhtmlテンプレートマークアップ全体([UserFirstName]、[UserLastName]などのプレースホルダーとともに)を保存するSQLサーバーの静的コンテンツでした。
次に、このデータをasp.netキャッシュにロードしました。したがって、実際に変更された場合にのみ、htmlテンプレートを何度も読み取らないでください。
クライアントにWYSIWYGエディターを提供して、管理Webフォームを介してこれらのテンプレートを変更しました。更新が行われるたびに、asp.netキャッシュをリセットします。
そして、メールログ用に別のテーブルがありました-送信されるすべてのメールがログに記録されました。このテーブルには、emailType、emailSent、numberOfTriesというフィールドがありました。
できるだけ早く送信する必要がある重要なメールタイプ(新しいメンバーのサインアップ、パスワードを忘れたなど)について、5分ごとにジョブを実行するだけです。
重要度の低いメールタイプ(プロモーションメール、ニュースメールなど)に対して15分ごとに別のジョブを実行しました
この方法では、サーバーがノンストップメールを送信することをブロックせず、バッチでメールを処理します。電子メールが送信されたら、emailSentフィールドを1に設定します。
どういうわけかテンプレートとして.ASPXページを使用し、そのコードをそのページに配信するようにコードに伝え、電子メールに返されたHTMLを使用することが理想的です。
WebRequestを簡単に作成してASPXページにアクセスし、結果のHTMLを取得できます。もう少し作業を行うと、おそらくWebRequestなしで完了できます。 PageParserとResponse.Filterを使用すると、ページを実行して出力をキャプチャできますが、もっとエレガントな方法もあります。
Aspxおよびascxソリューションは現在のHttpContextを必要とするため、多くの作業なしでは非同期に(スレッドで)使用できないことに注意してください。
簡単な答えはMvcMailerだと思います。お気に入りのビューエンジンを使用してメールを生成できるNuGetパッケージです。 NuGetパッケージ こちら および プロジェクトドキュメント をご覧ください
それが役に立てば幸い!
DotLiquidは別のオプションです。クラスモデルの値を{{ user.name }}
として指定し、実行時にそのクラスのデータとテンプレートにマークアップを指定すると、値がマージされます。 Razorテンプレートエンジンをさまざまな方法で使用することに似ています。ループのようなより複雑なものや、ToUpperのようなさまざまな機能をサポートしています。素晴らしいのは、これらが「安全」であるため、テンプレートを作成するユーザーがシステムをクラッシュさせたり、かみそりのように安全でないコードを記述したりできないことです。 http://dotliquidmarkup.org/try-online
ASPNETおよび関連するユーザーにファイルの読み取りおよび書き込みの許可を許可できる場合、標準のString.Format()
プレースホルダー(_{0}
_、_{1:C}
_などでHTMLファイルを簡単に使用できます。 。)これを達成する。
_System.IO
_名前空間のクラスを使用して、ファイルを文字列として読み込むだけです。その文字列を取得したら、String.Format()
の最初の引数として渡し、パラメータを提供します。
その文字列を保持し、それを電子メールの本文として使用すれば、本質的に完了です。私たちは今日、これを数十の(確かに小さな)サイトで行っており、問題はありませんでした。
(a)一度に無数の電子メールを送信していない場合、(b)各電子メールをパーソナライズしていない場合(そうでない場合は大量の文字列を食べている場合)、および(c )HTMLファイル自体は比較的小さいです。
TemplateMachine のようなテンプレートライブラリを使用します。これにより、通常は電子メールテンプレートを通常のテキストと一緒に配置し、ルールを使用して必要に応じて値を挿入/置換できます。 RubyのERBに非常に似ています。これにより、ASPXなどに過度に縛られることなく、メールコンテンツの生成を分離できます。これでコンテンツが生成されたら、メールで送信できます。
Canavarの答えに似ていますが、NVelocityの代わりに、構成ファイルからテンプレートを読み込むか、File.ReadAllText()を使用して外部ファイルを読み込み、値を設定する「 StringTemplate 」を常に使用します。
これはJavaプロジェクトですが、C#ポートはしっかりしており、いくつかのプロジェクトで使用しました(外部ファイルのテンプレートを使用したメールテンプレートに使用しました)。
選択肢は常に良いです。
Rajの答えが好きです。 ListManagerやDNNなどのフレームワークなどのプログラムは同様のことを行います。非技術ユーザーによる簡単な編集が必要な場合、SQLに保存されたHTMLを変更するWYSIWYGエディターは、最も簡単で簡単な方法であり、フッターとは無関係にヘッダーを簡単に編集できます。など、トークンを使用して動的に値を挿入します。
上記の方法(または実際に任意の方法)を使用する場合に留意すべきことの1つは、エディターに挿入を許可するスタイリングとタグのタイプを厳しく注意することです。ブラウザが扱いにくいと思われる場合は、電子メールクライアントが同じものをどのようにレンダリングするかがわかるまで待ってください...
SubSonic(www.subsonicproject.com)をご覧ください。彼らはまさにこれをしてコードを生成しています-テンプレートは標準ASPXであり、c#を出力します。同じ方法でシナリオを再利用できます。
私が使用しているライブラリをミックスに投げ込むだけです: https://github.com/lukencode/FluentEmail
RazorLight を使用して電子メールをレンダリングし、流fluentなスタイルを使用して電子メールを作成し、すぐに使用できる複数の送信者をサポートします。 ASP.NET DIの拡張メソッドも付属しています。プレーンテキストとHTMLをサポートする、使いやすく、セットアップが簡単です。
@bardevは優れたソリューションを提供しますが、残念ながらすべての場合に理想的ではありません。私もその一人です。
VS 2013では、WebフォームでWebFormsを使用しています(Webサイトを二度と使用しないことを誓います-これはPITAです)。
私はRazorの提案を試しましたが、私のWebサイトでは、IDEがMVCプロジェクトで提供する重要なIntelliSenseを取得できませんでした。また、テンプレートにデザイナーを使用したい-UserControlに最適なスポット。
かみそりに再びニックス。
そこで、代わりにこの小さなフレームワークを思いつきました(UserControlの@munとStrong Typingの@imatoriaのヒント)。私が見ることができる唯一の潜在的な問題点は、クラス名と.ASCXファイル名の同期を保つように注意する必要があるということです。外れると、ランタイムエラーが発生します。
FWIW:私のテストでは、少なくともRenderControl()呼び出しはページコントロールを好まないため、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} <[email protected]>".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
メールメッセージを設定しますIsBodyHtml = true
電子メールコンテンツを含むオブジェクトを取得し、オブジェクトをシリアル化し、xml/xsltを使用してhtmlコンテンツを生成します。
AlternateViewsを実行する場合は、jmeinが異なるxsltテンプレートを使用してプレーンテキストコンテンツを作成するのと同じことを行います。
これの主な利点の1つは、xsltテンプレートを更新するだけでレイアウトを変更したい場合です。
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など)でWebページのスタイルを表示するには、CSSをインライン化する必要があります。電子メールに動的コンテンツ(例:顧客名)が表示されている場合、WebサイトでQueryStringsを使用してデータを入力することをお勧めします。 (例 http://www.yourwebsite.com?CustomerName=Bob )