Могу ли я настроить HTML/ шаблоны электронной почты с ASP.NET?
Я работаю над сайтом, который будет рассылать значительное количество писем. Я хочу настроить текст верхнего и нижнего колонтитула или, возможно, даже шаблоны, чтобы пользователи могли легко редактировать эти электронные письма, если им это необходимо.
Если я вставлю HTML-код в строковые литералы C#, это будет ужасно, и им придется беспокоиться о побеге. Включение плоских файлов для верхнего и нижнего колонтитула может сработать, но что-то в этом не так.
Что было бы идеально, что бы использовать .ASPX
страница как шаблон, а затем просто скажите моему коду, чтобы он обслуживал эту страницу, и используйте HTML, возвращенный для электронной почты.
Есть ли хороший и простой способ сделать это? Есть ли лучший способ решить эту проблему?
Обновлено:
Я добавил ответ, который позволяет использовать стандартную страницу.aspx в качестве шаблона электронной почты. Просто замените все переменные, как обычно, используйте привязку данных и т. Д. Затем просто запишите выходные данные страницы и вуаля! У вас есть ваш HTML-адрес электронной почты!
ОБНОВЛЕНО CAVEAT!!!
Я отлично использовал класс MailDefinition на некоторых страницах aspx, но при попытке использовать этот класс во время запущенного серверного процесса он не удался. Я полагаю, что это потому, что метод MailDefinition.CreateMailMessage() требует действительный элемент управления для ссылки, даже если он не всегда что-то делает. Из-за этого я бы порекомендовал мой подход с использованием страницы aspx или подход Маня с использованием страницы ascx, что кажется немного лучше.
23 ответа
Здесь уже есть масса ответов, но я наткнулся на отличную статью о том, как использовать Razor с шаблонами писем. Razor был перенесен с ASP.NET MVC 3, но MVC не требуется использовать Razor. Это довольно приятная обработка шаблонов электронной почты
Как указывается в статье: "Лучшее в Razor - это то, что в отличие от его предшественника (веб-форм) он не связан с веб-средой, мы можем легко разместить его вне сети и использовать в качестве движка шаблонов для различных целей".
Генерация электронных писем в формате HTML с помощью RazorEngine - Часть 01 - Введение
Использование шаблонов Razor за пределами ASP.NET: они больше не только для HTML!
Более умные шаблоны электронной почты в ASP.NET с RazorEngine
Подобный Stackru QA
Шаблонирование с использованием нового API RazorEngine
Можно ли использовать Razor View Engine за пределами asp.net?
Вы также можете попробовать загрузить элемент управления, а затем отобразить его в виде строки и установить его в качестве тела 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("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);
Пользовательский элемент управления может содержать другие элементы управления, такие как верхний и нижний колонтитулы, а также использовать преимущества таких функций, как привязка данных.
Если вы хотите передать такие параметры, как имена пользователей, названия продуктов и т. Д., Вы можете использовать движок шаблонов с открытым исходным кодом 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
[Дата: 01.03.2009] Имя: Пункт 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("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 и попробуйте.
Обратите внимание, что это коммерческий продукт, который я создал.
Если гибкость является одним из ваших предварительных условий, XSLT может быть хорошим выбором, который полностью поддерживается.NET Framework, и вы сможете даже позволить пользователю редактировать эти файлы. Эта статья ( http://www.aspfree.com/c/a/XML/XSL-Transformations-using-ASP-NET/) может быть полезна для начала (msdn имеет больше информации об этом). По словам ScarletGarden, NVelocity - еще один хороший выбор, но я предпочитаю XSLT за его "встроенную" поддержку.NET Framework и независимость от платформы.
Я думаю, что вы также можете сделать что-то вроде этого:
Создайте и страницу ASPX, и поместите это в конец метода OnLoad, или вызовите его вручную.
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
HtmlTextWriter htmlTW = new HtmlTextWriter(sw);
this.Render(htmlTW);
Я не уверен, есть ли потенциальные проблемы с этим, но похоже, что это будет работать. Таким образом, вы можете использовать полнофункциональную страницу.aspx вместо класса MailDefinition, который поддерживает только замены текста.
Вот еще одна альтернатива, которая использует преобразования XSL для более сложных шаблонов электронной почты: Отправка электронной почты на основе HTML из приложений.NET.
Конечно, вы можете создать 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("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);
При этом будьте осторожны, спам-фильтры, похоже, блокируют сгенерированный ASP.net html, по-видимому, из-за ViewState, поэтому, если вы собираетесь это сделать, убедитесь, что полученный html чистый.
Я лично хотел бы использовать Asp.net MVC для достижения желаемых результатов. или NVelocity довольно хорош в этом
Что было бы идеально, так это как-то использовать страницу.ASPX в качестве шаблона, а затем просто указать мой код для обслуживания этой страницы и использовать HTML-код, возвращаемый для электронной почты.
Вы можете легко создать WebRequest, чтобы попасть на страницу ASPX и получить результирующий HTML. Приложив немного больше работы, вы, вероятно, сможете сделать это без WebRequest. PageParser и Response.Filter позволят вам запустить страницу и захватить вывод... хотя могут быть и более элегантные способы.
Я думаю, что простой ответ - MvcMailer. Это пакет NuGet, который позволяет вам использовать ваш любимый движок для создания электронных писем. Смотрите пакет NuGet здесь и проектную документацию
Надеюсь, поможет!
Обратите внимание, что для решений aspx и ascx требуется текущий HttpContext, поэтому его нельзя использовать асинхронно (например, в потоках) без большой работы.
Просто добавление библиотеки, которую я использую, в микс: https://github.com/lukencode/FluentEmail
Он отображает электронные письма с помощью RazorLight, использует свободный стиль для создания электронных писем и поддерживает несколько отправителей из коробки. Он также поставляется с методами расширения для ASP.NET DI. Простая в использовании, небольшая настройка, с поддержкой обычного текста и HTML.
У меня было аналогичное требование к одному из проектов, где вам приходилось отправлять огромное количество писем каждый день, и клиент хотел получить полный контроль над HTML-шаблонами для разных типов писем.
из-за большого количества отправляемых электронных писем, первостепенной проблемой была производительность.
мы создали статический контент на сервере SQL, где вы сохраняете разметку всего HTML-шаблона (вместе с заполнителями, такими как [UserFirstName], [UserLastName], которые заменяются реальными данными во время выполнения) для различных типов электронных писем.
затем мы загрузили эти данные в кэш asp.net - поэтому мы не читаем html-шаблоны снова и снова - но только когда они действительно изменяются
мы предоставили клиенту WYSIWYG-редактор для изменения этих шаблонов через веб-форму администратора. всякий раз, когда были сделаны обновления, мы сбрасывали кэш asp.net.
и затем у нас была отдельная таблица для журналов электронной почты - где регистрировалось каждое отправляемое письмо. в этой таблице есть поля с именами emailType, emailSent и numberOfTries.
мы просто запускали работу каждые 5 минут для важных типов электронной почты (например, регистрация нового участника, забытый пароль), которые необходимо отправить как можно скорее.
каждые 15 минут мы выполняли другую работу для менее важных типов электронной почты (таких как рекламные сообщения, новости и т. д.)
Таким образом, вы не блокируете отправку сервером своих сообщений без остановки и обрабатываете их в пакетном режиме. как только письмо отправлено, вы устанавливаете в поле emailSent значение 1.
Посмотрите на SubSonic (www.subsonicproject.com). Они делают именно это для генерации кода - шаблон является стандартным ASPX, и он выводит C#. Тот же метод будет многократно использоваться для вашего сценария.
DotLiquid - еще один вариант. Вы указываете значения из модели класса как {{ user.name }}
а затем во время выполнения вы предоставляете данные в этом классе и шаблон с разметкой, и он объединит значения для вас. Это во многом похоже на использование движка шаблонов Razor. Он поддерживает более сложные вещи, такие как циклы и различные функции, такие как ToUpper. Хорошая вещь в том, что они "безопасны", так что пользователь, который создает шаблоны, не может сломать вашу систему или написать небезопасный код, как вы это сделали в бритве: http://dotliquidmarkup.org/try-online
Я бы использовал библиотеку шаблонов, такую как TemplateMachine. это позволяет вам в основном составлять шаблон электронной почты вместе с обычным текстом, а затем использовать правила для ввода / замены значений по мере необходимости. Очень похоже на ERB в Ruby. Это позволяет вам отделить генерацию почтового контента, не привязывая вас слишком сильно к чему-то вроде ASPX и т. Д., И после того, как контент сгенерирован с этим, вы можете отправить электронное письмо.
Мне нравится ответ Радж. Такие программы, как ListManager, и фреймворки, такие как DNN, выполняют аналогичные функции, и, если требуется простое редактирование пользователями, не являющимися техническими специалистами, редакторы WYSIWYG для изменения HTML-кода, хранящегося в SQL, - это в основном простой и понятный способ, позволяющий легко редактировать заголовки независимо от колонтитулов и т.д., а также использование токенов для динамического добавления значений.
При использовании вышеуказанного метода (или любого другого) следует помнить о строгости и осторожности в отношении того, какие типы стилей и тегов вы разрешаете редакторам вставлять. Если вы думаете, что браузеры привередливы, просто подождите, пока не увидите, как по-разному почтовые клиенты отображают одно и то же...
Установите набор сообщений электронной почты IsBodyHtml = true
Возьмите ваш объект, который содержит содержимое вашей электронной почты. Сериализуйте объект и используйте xml / xslt для генерации html-содержимого.
Если вы хотите сделать AlternateViews, сделайте то же самое, что jmein использует только другой шаблон xslt для создания простого текстового содержимого.
Одним из основных преимуществ является то, что если вы хотите изменить свой макет, все, что вам нужно сделать, обновить шаблон xslt.
@bardev предлагает хорошее решение, но, к сожалению, оно не идеально во всех случаях. Мой был одним из них.
Я использую веб-формы на веб-сайте (клянусь, я никогда не буду использовать веб-сайт снова - что такое PITA) в VS 2013.
Я попробовал предложение Razor, но, поскольку я был веб-сайтом, я не получил все необходимое IntelliSense, которое IDE предоставляет в проекте MVC. Мне также нравится использовать дизайнер для моих шаблонов - идеальное место для UserControl.
Никс на Бритве снова.
Так что вместо этого я придумал этот небольшой фреймворк (с советами к @mun для UserControl и @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://stackru.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
Если вы можете разрешить ASPNET и связанным пользователям разрешение на чтение и запись файла, вы можете легко использовать файл HTML со стандартным String.Format()
заполнители ({0}
, {1:C}
и т. д.) для достижения этой цели.
Просто читать в файле, как строку, используя классы из System.IO
Пространство имен. Получив эту строку, передайте ее в качестве первого аргумента String.Format()
и предоставить параметры.
Держите эту строку и используйте ее в качестве основной части электронного письма, и вы, по сути, сделали это. Мы делаем это на десятках (по общему признанию, небольших) сайтов сегодня, и у нас не было проблем.
Я должен отметить, что это работает лучше всего, если (а) вы не отправляете миллионы электронных писем одновременно, (б) вы не персонализируете каждое электронное письмо (в противном случае вы съедаете тонну строк) и (с) сам файл HTML относительно невелик.
Вот простой способ использования класса 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)
Аналогично ответу Канавара, но вместо NVelocity я всегда использую " StringTemplate", который загружает шаблон из файла конфигурации, или загружаю внешний файл с помощью File.ReadAllText() и устанавливаю значения.
Это проект Java, но порт C# является надежным, и я использовал его в нескольких проектах (просто использовал его для шаблонов писем, используя шаблон во внешнем файле).
Альтернативы всегда хороши.