Могу ли я настроить HTML/Email Templates с помощью ASP.NET?

Я работаю над сайтом, который отправит значительное количество писем. Я хочу настроить как текст заголовка, так и нижний колонтитул, или, возможно, даже шаблоны, чтобы пользователи могли легко редактировать эти письма, если им нужно.

Если я внедряю HTML внутри строковых литералов С#, это уродливо, и им придется беспокоиться об экранировании. Включение плоских файлов для заголовка и нижнего колонтитула может работать, но что-то об этом просто не кажется правильным.

Что было бы идеальным в том, чтобы каким-то образом использовать страницу .ASPX в качестве шаблона, а затем просто скажите моему коду, чтобы он обслуживал эту страницу, и используйте HTML, возвращенный для письма.

Есть ли хороший и простой способ сделать это? Есть ли лучший способ решить эту проблему?

Обновлено:
Я добавил ответ, который позволяет использовать стандартную страницу .aspx в качестве шаблона электронной почты. Просто замените все переменные, как обычно, используйте привязку данных и т.д. Затем просто запишите вывод страницы и вуаля! У вас есть HTML-адрес электронной почты!

ОБНОВЛЕНО С ПЕЩЕЙ!!!:
Я очень хорошо использовал класс MailDefinition на некоторых страницах aspx, но при попытке использовать этот класс во время выполняемого серверного процесса он не удался. Я считаю, что это связано с тем, что для метода MailDefinition.CreateMailMessage() требуется действительный элемент управления для ссылки, хотя он не всегда что-то делает. Из-за этого я бы рекомендовал мой подход, используя страницу aspx, или подход Mun, используя страницу ascx, которая кажется немного лучше.

Ответ 1

Там уже много ответов, но я наткнулся на отличную статью о том, как использовать Razor с шаблонами электронной почты. Razor был перенесен с ASP.NET MVC 3, но MVC не требуется использовать Razor. Это довольно гладкая обработка шаблонов электронной почты

Как отмечается в статье, "Лучшая вещь Razor заключается в том, что в отличие от своего предшественника (веб-формы) она не связана с веб-средой, мы можем легко разместить ее за пределами Интернета и использовать ее в качестве механизма шаблонов для различных целей".

Создание электронных писем HTML с помощью RazorEngine - Часть 01 - Введение

Использование шаблонов Razor за пределами ASP.NET: Theyre не только для HTML больше!

Умные шаблоны электронной почты в ASP.NET с RazorEngine

Аналогичный QA

Шаблоны с использованием нового API RazorEngine

Использование Razor без MVC

Можно ли использовать Razor View Engine вне asp.net

Ответ 2

Вы также можете попробовать загрузить элемент управления, а затем передать его в строку и установить это как тело 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();

Затем вы можете создать свой адрес электронной почты, как обычно:

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

Пользовательский элемент управления может содержать другие элементы управления, такие как верхний и нижний колонтитулы, а также использовать такие функции, как привязка данных.

Ответ 4

Если вы хотите передавать такие параметры, как имена пользователей, названия продуктов и т.д., вы можете использовать механизм шаблонов с открытым исходным кодом 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.

Пример:

[Дата: 12.02.2009] Имя: Пункт 1, Значение: 09

[Дата: 21.02.2009] Имя: Пункт 4, Значение: 52

[Дата: 01.03.2009] Имя: Пункт 2, Значение: 21

[Дата: 23.03.2009] Имя: Пункт 6, Значение: 24

Для редактирования шаблонов, вы можете использовать FCKEditor и сохранять свои шаблоны в файлах.

Ответ 5

Компонент электронной почты Mail.dll включает механизм шаблонов электронной почты:

Вот обзор синтаксиса:

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

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

</body>
</html>

И код, загружающий шаблон, заполняет данные из объекта С# и отправляет электронное письмо:

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 и попробуйте.

Обратите внимание, что это коммерческий продукт, который я создал.

Ответ 6

Если гибкость является одним из ваших предварительных условий, XSLT может быть хорошим выбором, который полностью поддерживается платформой .NET, и вы сможете даже позволить пользователю редактировать эти файлы. Эта статья (http://www.aspfree.com/c/a/XML/XSL-Transformations-using-ASP-NET/) может быть полезна для запуска (у msdn есть больше информации об этом). Как сказал ScarletGarden, NVelocity - еще один хороший выбор, но я предпочитаю XSLT для его "встроенной" поддержки платформы .NET и агностики платформы.

Ответ 7

Думаю, вы могли бы сделать что-то вроде этого:

Создайте и .aspx-страницу и поместите это в конец метода OnLoad или вызовите его вручную.

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

Я не уверен, есть ли какие-то потенциальные проблемы с этим, но похоже, что это сработает. Таким образом, вы можете использовать полнофункциональную страницу .aspx вместо класса MailDefinition, которая поддерживает только замену текста.

Ответ 8

Конечно, вы можете создать 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);

Ответ 10

Будьте осторожны, при этом фильтры SPAM, похоже, блокируют генерируемый html ASP.net, по-видимому, из-за ViewState, поэтому, если вы сделаете это, убедитесь, что полученный Html чистый.

Я лично рассмотрел бы использование Asp.net MVC для достижения желаемых результатов. или NVelocity неплохо в этом

Ответ 11

Что было бы идеальным, чтобы каким-то образом использовать страницу .ASPX как шаблон, а затем просто сообщите моему коду, чтобы он обслуживал эту страницу, и используйте HTML-код, возвращенный для письма.

Вы можете просто построить WebRequest, чтобы попасть на страницу ASPX и получить полученный HTML-код. С немного больше работы, вы можете, возможно, сделать это без WebRequest. PageParser и Response.Filter позволяют вам запускать страницу и захватывать результат... хотя могут быть и более элегантные способы.

Ответ 12

У меня было аналогичное требование в 1 из проектов, где каждый день приходилось отправлять огромное количество писем, и клиент хотел получить полный контроль над шаблонами html для разных типов электронных писем.

из-за большого количества отправляемых писем, производительность была главной проблемой.

то, что мы придумали, - это статический контент на сервере sql, где вы сохраняете весь шаблон шаблона html (вместе с владельцами мест, такими как [UserFirstName], [UserLastName], которые заменяются реальными данными во время выполнения) для разных типов электронная почта

тогда мы загрузили эти данные в кеш-память asp.net - поэтому мы не читаем html-шаблоны снова и снова, но только тогда, когда они действительно изменены

мы предоставили клиенту редактор WYSIWYG для изменения этих шаблонов через веб-форму администратора. всякий раз, когда делались обновления, мы reset кеш asp.net.

а затем у нас была отдельная таблица для журналов электронной почты - где было отправлено каждое отправленное письмо. в этой таблице были поля, называемые emailType, emailSent и numberOfTries.

мы просто запускали работу каждые 5 минут для важных типов электронной почты (например, зарегистрировать нового участника, забыли пароль), которые нужно отправить asap

мы запускали еще одну работу каждые 15 минут для менее важных типов электронной почты (например, для рассылки, электронной почты и т.д.)

Таким образом, вы не блокируете отправку сервера без остановок электронной почты и обрабатываете письма в пакетном режиме. после отправки электронной почты вы установите для поля emailSent значение 1.

Ответ 13

Обратите внимание, что решения aspx и ascx требуют текущего HttpContext, поэтому не могут использоваться асинхронно (например, в потоках) без большой работы.

Ответ 14

Я думаю, что легкий ответ - MvcMailer. Это пакет NuGet, который позволяет использовать ваш любимый механизм просмотра для создания электронных писем. См. Пакет NuGet здесь и документация по проекту

Надеюсь, что это поможет!

Ответ 15

DotLiquid - еще один вариант. Вы указываете значения из модели класса как {{ user.name }}, а затем во время выполнения вы предоставляете данные в этом классе и шаблон с разметкой, и он будет объединять значения для вас. Он похож на использование шаблона шаблонов Razor разными способами. Он поддерживает более сложные вещи, такие как циклы и различные функции, такие как ToUpper. Самое приятное, что они "безопасны", так что пользователь, создающий шаблоны, не может разрушить вашу систему или написать небезопасный код, как в бритве: http://dotliquidmarkup.org/try-online

Ответ 16

Если вы можете разрешить ASPNET и связанным пользователям разрешать чтение и запись файла, вы можете легко использовать HTML файл со стандартными String.Format() заполнителями ({0}, {1:C} и т.д.), чтобы выполнить это,

Просто прочтите в файле как строку, используя классы из пространства имен System.IO. После того, как у вас есть эта строка, передайте ее как первый аргумент String.Format() и укажите параметры.

Сохраните эту строку и используйте ее как тело электронной почты, и вы по существу делаете это. Мы делаем это на десятках (по общему признанию, небольших) сайтов сегодня и не испытывали никаких проблем.

Я должен отметить, что это лучше всего работает, если (а) вы не отправляете по-разному числа писем по электронной почте, (б) вы не персонализируете каждое электронное письмо (иначе вы едите тонну строк) и (c) сам файл HTML относительно невелик.

Ответ 17

Задайте набор сообщений электронной почты IsBodyHtml = true

Возьмите свой объект, содержащий содержимое электронной почты. Сериализуйте объект и используйте xml/xslt для генерации содержимого html.

Если вы хотите сделать AlternateView, сделайте то же самое, что jmein использует только другой шаблон xslt для создания текстового содержимого.

Одним из основных преимуществ этого является то, что если вы хотите изменить свой макет, все, что вам нужно, обновить шаблон xslt.

Ответ 18

Посмотрите на SubSonic (www.subsonicproject.com). Они делают именно это для генерации кода - шаблон является стандартным ASPX, и он выводит С#. Тот же метод будет использоваться повторно для вашего сценария.

Ответ 19

Я бы использовал библиотеку шаблонов, например TemplateMachine. это позволяет в основном разместить ваш шаблон электронной почты вместе с обычным текстом, а затем использовать правила для ввода/замены значений по мере необходимости. Очень похож на ERB в Ruby. Это позволяет вам отделить создание почтового содержимого, не связывая вас слишком сильно с чем-то вроде ASPX и т.д., После того как контент будет создан с этим, вы можете отправить его по электронной почте.

Ответ 20

Мне нравится ответ Радж. Такие программы, как ListManager и фреймворки, такие как DNN, делают подобные вещи, и если требуется простое редактирование нетехническими пользователями, редакторы WYSIWYG для изменения HTML, хранящихся в SQL, - это, в основном, простой и понятный способ и могут легко размещать заголовки редактирования независимо от нижних колонтитулов, и т.д., а также использование токенов для динамической вставки значений.

Одна вещь, которую следует иметь в виду, если использовать вышеприведенный метод (или любой, действительно), быть строгим и осторожным в отношении того, какие типы стилей и тегов вы позволяете редакторам вставлять. Если вы считаете, что браузеры являются точными, просто подождите, пока не увидите, как по-разному клиенты электронной почты выполняют одно и то же...

Ответ 21

Как и ответ Canavar, но вместо NVelocity я всегда использую " StringTemplate" который загружает шаблон из файла конфигурации или загружает внешний файл с помощью File.ReadAllText() и устанавливает значения.

Это проект Java, но порт С# является солидным, и я использовал его в нескольких проектах (просто использовал его для шаблонов электронной почты, используя шаблон во внешнем файле).

Альтернативы всегда хороши.

Ответ 22

Вот простой способ использования класса 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), ваш CSS должен быть выровнен. Если ваше электронное письмо отображает динамический контент (например, имя клиента), я бы рекомендовал использовать QueryStrings на вашем веб-сайте для заполнения данных. (например, http://www.yourwebsite.com?CustomerName=Bob)

Ответ 23

@bardev обеспечивает хорошее решение, но, к сожалению, он не идеален во всех случаях. Мой был одним из них.

Я использую WebForms на веб-сайте (клянусь, я больше никогда не буду использовать веб-сайт - что такое PITA) на VS 2013.

Я попробовал предложение Razor, но мой сайт стал не самым важным IntelliSense, который IDE предоставляет в проекте MVC. Мне также нравится использовать конструктор для моих шаблонов - идеальное место для UserControl.

Nix на Razor снова.

Итак, я придумал эту небольшую основу (подсказка для @mun для UserControl и @imatoria для сильной печати). Почти единственное потенциальное препятствие, которое я вижу, заключается в том, что вы должны быть осторожны, чтобы сохранить имя файла .ASCX в синхронизации с его именем класса. Если вы отклонились, вы получите ошибку времени выполнения.

FWIW: В моем тестировании, по крайней мере, вызов RenderControl() не нравится управление страницей, поэтому я пошел с 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} <[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

"Factory" Класс:

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

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

Ответ 24

Просто добавляю библиотеку, которую я использую, в микс: https://github.com/lukencode/FluentEmail

Он обрабатывает электронные письма, используя RazorLight, использует свободный стиль для создания электронных писем и поддерживает сразу несколько отправителей. Он также поставляется с методами расширения для ASP.NET DI. Простая в использовании, небольшая настройка, с простым текстом и поддержкой HTML.