web-dev-qa-db-ja.com

ASP.NETでHTML /電子メールテンプレートを設定できますか?

私はかなりの数のメールを送信するサイトに取り組んでいます。ユーザーが必要に応じてこれらのメールを簡単に編集できるように、ヘッダーテキストとフッターテキストの両方、またはテンプレートを設定したいです。

HTMLをC#文字列リテラル内に埋め込むと、見苦しくなり、エスケープを心配する必要があります。ヘッダーとフッターにフラットファイルを含めることは機能するかもしれませんが、それについて何かが正しく感じられません。

どういうわけか.ASPXページをテンプレートとして使用し、そのコードをそのページに配信するように伝え、メールに返されたHTMLを使用することが理想的です。

これを行うための素敵で簡単な方法はありますか?この問題を解決するためのより良い方法はありますか?

更新:
標準の.aspxページをメールテンプレートとして使用できるようにする回答を追加しました。通常どおりすべての変数を置き換え、データバインディングなどを使用します。その後、ページの出力をキャプチャして、出来上がります。 HTMLメールがあります!

警告付きで更新!!!:
一部のaspxページでMailDefinitionクラスを使用していましたが、実行中のサーバープロセス中にこのクラスを使用しようとして失敗しました。 MailDefinition.CreateMailMessage()メソッドは、常に何かをするわけではありませんが、参照するには有効なコントロールが必要だからだと思います。このため、aspxページを使用する私のアプローチ、またはascxページを使用するMunのアプローチをお勧めします。

94
John Bubriski

ここにはたくさんの答えがありますが、私はRazorを電子メールテンプレートで使用する方法についてのすばらしい記事を見つけました。 RazorはASP.NET MVC 3でプッシュされましたが、MVCはRazorを使用するために必須ではありません。これは、メールテンプレートを実行する非常に洗練された処理です。

記事が特定するように、「Razorの最大の利点は、その前身(webforms)とは異なり、Web環境と結び付けられていないため、Webの外部で簡単にホストし、さまざまな目的のテンプレートエンジンとして使用できることです。」

RazorEngineを使用したHTMLメールの生成-パート01-はじめに

ASP.NET外でのRazorテンプレートの活用:HTMLだけではありません!

RazorEngineを使用したASP.NETのスマートな電子メールテンプレート

同様のStackoverflow QA

新しいRazorEngine APIを使用したテンプレート化

MVCなしでRazorを使用

asp.netの外でRazor View Engineを使用することは可能ですか

71

また、コントロールをロードし、それを文字列にレンダリングして、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);

ユーザーコントロールには、ヘッダーやフッターなどの他のコントロールを含めることができ、データバインディングなどの機能も利用できます。

55
Mun

MailDefinition class を試すことができます

35
John Sheehan

ユーザー名、製品名などのパラメータを渡したい場合は、オープンソースのテンプレートエンジン 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 を使用して、テンプレートをファイルに保存できます。

18
Canavar

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電子メールコンポーネント をダウンロードして、試してみてください。

これは私が作成した商用製品であることに注意してください。

7

柔軟性が前提条件の1つである場合、XSLTが適切な選択である可能性があります。XSLTは.NETフレームワークで完全にサポートされており、ユーザーがそれらのファイルを編集できるようにすることもできます。この記事( http://www.aspfree.com/c/a/XML/XSL-Transformations-using-ASP-NET/ )は、開始に役立つ場合があります(msdnに詳細があります) )。 ScarletGarden NVelocityによると別の良い選択ですが、「組み込み」の.NETフレームワークのサポートとプラットフォームに依存しないXSLTを好みます。

6
Everton

次のようなこともできると思います。

.aspxページを作成し、これをOnLoadメソッドの最後に配置するか、手動で呼び出します。

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

これに潜在的な問題があるかどうかはわかりませんが、うまくいくようです。この方法では、テキストの置換のみをサポートするMailDefinitionクラスの代わりに、フル機能の.aspxページを使用できます。

5
John Bubriski

より複雑な電子メールテンプレートにXSL変換を使用するもう1つの代替方法を次に示します。 。NETアプリケーションからのHTMLベースの電子メールの送信

4
Alek Davis

確かに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);
4
Josh Mein

これを行う際には、ViewStateのせいで、スパムフィルターがASP.netで生成されたhtmlをブロックするように注意してください。

個人的には、Asp.net MVCを使用して目的の結果を達成することを検討します。または NVelocity はこれでかなり良い

2
danswain

毎日膨大な数のメールを送信する必要があるプロジェクトの1つで同様の要件があり、クライアントはさまざまな種類のメールのhtmlテンプレートを完全に制御することを望んでいました。

送信される電子メールの数が多いため、パフォーマンスが主な関心事でした。

私たちが思いついたのは、さまざまなタイプの電子メール用のhtmlテンプレートマークアップ全体([UserFirstName]、[UserLastName]などのプレースホルダーとともに)を保存するSQLサーバーの静的コンテンツでした。

次に、このデータをasp.netキャッシュにロードしました。したがって、実際に変更された場合にのみ、htmlテンプレートを何度も読み取らないでください。

クライアントにWYSIWYGエディターを提供して、管理Webフォームを介してこれらのテンプレートを変更しました。更新が行われるたびに、asp.netキャッシュをリセットします。

そして、メールログ用に別のテーブルがありました-送信されるすべてのメールがログに記録されました。このテーブルには、emailType、emailSent、numberOfTriesというフィールドがありました。

できるだけ早く送信する必要がある重要なメールタイプ(新しいメンバーのサインアップ、パスワードを忘れたなど)について、5分ごとにジョブを実行するだけです。

重要度の低いメールタイプ(プロモーションメール、ニュースメールなど)に対して15分ごとに別のジョブを実行しました

この方法では、サーバーがノンストップメールを送信することをブロックせず、バッチでメールを処理します。電子メールが送信されたら、emailSentフィールドを1に設定します。

1
Raj

どういうわけかテンプレートとして.ASPXページを使用し、そのコードをそのページに配信するようにコードに伝え、電子メールに返されたHTMLを使用することが理想的です。

WebRequestを簡単に作成してASPXページにアクセスし、結果のHTMLを取得できます。もう少し作業を行うと、おそらくWebRequestなしで完了できます。 PageParserとResponse.Filterを使用すると、ページを実行して出力をキャプチャできますが、もっとエレガントな方法もあります。

1
Mark Brackett

Aspxおよびascxソリューションは現在のHttpContextを必要とするため、多くの作業なしでは非同期に(スレッドで)使用できないことに注意してください。

1
Rosco

簡単な答えはMvcMailerだと思います。お気に入りのビューエンジンを使用してメールを生成できるNuGetパッケージです。 NuGetパッケージ こちら および プロジェクトドキュメント をご覧ください

それが役に立てば幸い!

1
Sohan

DotLiquidは別のオプションです。クラスモデルの値を{{ user.name }}として指定し、実行時にそのクラスのデータとテンプレートにマークアップを指定すると、値がマージされます。 Razorテンプレートエンジンをさまざまな方法で使用することに似ています。ループのようなより複雑なものや、ToUpperのようなさまざまな機能をサポートしています。素晴らしいのは、これらが「安全」であるため、テンプレートを作成するユーザーがシステムをクラッシュさせたり、かみそりのように安全でないコードを記述したりできないことです。 http://dotliquidmarkup.org/try-online

1
AaronLS

ASPNETおよび関連するユーザーにファイルの読み取りおよび書き込みの許可を許可できる場合、標準のString.Format()プレースホルダー(_{0}_、_{1:C}_などでHTMLファイルを簡単に使用できます。 。)これを達成する。

_System.IO_名前空間のクラスを使用して、ファイルを文字列として読み込むだけです。その文字列を取得したら、String.Format()の最初の引数として渡し、パラメータを提供します。

その文字列を保持し、それを電子メールの本文として使用すれば、本質的に完了です。私たちは今日、これを数十の(確かに小さな)サイトで行っており、問題はありませんでした。

(a)一度に無数の電子メールを送信していない場合、(b)各電子メールをパーソナライズしていない場合(そうでない場合は大量の文字列を食べている場合)、および(c )HTMLファイル自体は比較的小さいです。

0
John Rudy

TemplateMachine のようなテンプレートライブラリを使用します。これにより、通常は電子メールテンプレートを通常のテキストと一緒に配置し、ルールを使用して必要に応じて値を挿入/置換できます。 RubyのERBに非常に似ています。これにより、ASPXなどに過度に縛られることなく、メールコンテンツの生成を分離できます。これでコンテンツが生成されたら、メールで送信できます。

0
MikeJ

Canavarの答えに似ていますが、NVelocityの代わりに、構成ファイルからテンプレートを読み込むか、File.ReadAllText()を使用して外部ファイルを読み込み、値を設定する「 StringTemplate 」を常に使用します。

これはJavaプロジェクトですが、C#ポートはしっかりしており、いくつかのプロジェクトで使用しました(外部ファイルのテンプレートを使用したメールテンプレートに使用しました)。

選択肢は常に良いです。

0

Rajの答えが好きです。 ListManagerやDNNなどのフレームワークなどのプログラムは同様のことを行います。非技術ユーザーによる簡単な編集が必要な場合、SQLに保存されたHTMLを変更するWYSIWYGエディターは、最も簡単で簡単な方法であり、フッターとは無関係にヘッダーを簡単に編集できます。など、トークンを使用して動的に値を挿入します。

上記の方法(または実際に任意の方法)を使用する場合に留意すべきことの1つは、エディターに挿入を許可するスタイリングとタグのタイプを厳しく注意することです。ブラウザが扱いにくいと思われる場合は、電子メールクライアントが同じものをどのようにレンダリングするかがわかるまで待ってください...

0
Nick

SubSonic(www.subsonicproject.com)をご覧ください。彼らはまさにこれをしてコードを生成しています-テンプレートは標準ASPXであり、c#を出力します。同じ方法でシナリオを再利用できます。

0
jvenema

私が使用しているライブラリをミックスに投げ込むだけです: https://github.com/lukencode/FluentEmail

RazorLight を使用して電子メールをレンダリングし、流fluentなスタイルを使用して電子メールを作成し、すぐに使用できる複数の送信者をサポートします。 ASP.NET DIの拡張メソッドも付属しています。プレーンテキストとHTMLをサポートする、使いやすく、セットアップが簡単です。

0
ahong

@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
0
InteXX

メールメッセージを設定しますIsBodyHtml = true

電子メールコンテンツを含むオブジェクトを取得し、オブジェクトをシリアル化し、xml/xsltを使用してhtmlコンテンツを生成します。

AlternateViewsを実行する場合は、jmeinが異なるxsltテンプレートを使用してプレーンテキストコンテンツを作成するのと同じことを行います。

これの主な利点の1つは、xsltテンプレートを更新するだけでレイアウトを変更したい場合です。

0
Bob The Janitor

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

0
ROFLwTIME