Создание тела электронного письма в формате HTML на C #

114

Есть ли лучший способ сгенерировать электронную почту HTML на C # (для отправки через System.Net.Mail), чем использовать Stringbuilder для выполнения следующих действий:

string userName = "John Doe";
StringBuilder mailBody = new StringBuilder();
mailBody.AppendFormat("<h1>Heading Here</h1>");
mailBody.AppendFormat("Dear {0}," userName);
mailBody.AppendFormat("<br />");
mailBody.AppendFormat("<p>First part of the email body goes here</p>");

и так далее и тому подобное?

Роб
источник
Что ж, это действительно зависит от решения, как я его вижу. Я сделал все: от получения пользовательского ввода до автоматического форматирования по разным шаблонам. Лучшее решение, которое я сделал с почтовыми сообщениями html, было на самом деле форматированием xml + xslt, поскольку мы знали ввод почты заранее.
Cyberzed
Это зависит от сложности ваших требований. Однажды у меня было приложение, которое отображало таблицу в электронном письме в формате HTML, и я использовал ASP.NET Gridview для визуализации конкатенационных строк HTML для создания таблицы. Это было бы беспорядочно.
RichardOD

Ответы:

181

Вы можете использовать класс MailDefinition .

Вот как вы его используете:

MailDefinition md = new MailDefinition();
md.From = "test@domain.com";
md.IsBodyHtml = true;
md.Subject = "Test of MailDefinition";

ListDictionary replacements = new ListDictionary();
replacements.Add("{name}", "Martin");
replacements.Add("{country}", "Denmark");

string body = "<div>Hello {name} You're from {country}.</div>";

MailMessage msg = md.CreateMailMessage("you@anywhere.com", replacements, body, new System.Web.UI.Control());

Кроме того, я написал сообщение в блоге о том, как сгенерировать тело сообщения электронной почты в формате HTML на C # с использованием шаблонов с использованием класса MailDefinition .

MartinHN
источник
11
Я даже не знал, что это существует, это гениально ... +1 и принято
Роб
5
+1. Хороший, хотя и ограниченный, вероятно, подходит для многих целей. Не очень полезно, если вы хотите программно включать разделы HTML и / или перебирать набор элементов, требующих рендеринга.
AnthonyWJones,
Я узнал об этом совсем недавно. Это круто. Я полагаю, это говорит вам, насколько важно взглянуть на документацию MSDN, прежде чем самостоятельно писать класс для любой проблемы. Я написал свой собственный класс, который делал почти то же самое, что и MailDefinition. Жалко для меня. Пустая трата времени.
MartinHN
4
Мне было неудобно использовать класс MailDefinition из-за ограниченных возможностей указания полей from, to, cc и bcc. Он также полагается на пространство имен для элементов управления веб-интерфейсом - для меня это не имеет смысла. Смотрите мой ответ ниже ...
Сет
+1, поскольку я не знал, что этот класс существует, хотя базовая реализация просто выполняет итерацию по списку замен и выполняется Regex.Replace(body, pattern, replacement, RegexOptions.IgnoreCase);для каждой пары ключ / значение ... в свете этой детали этот класс не имеет большого значения, как использовалось выше если не работаете с существующей ссылкой на System.Web.UI.Controls.
Крис Бакстер,
27

Используйте класс System.Web.UI.HtmlTextWriter.

StringWriter writer = new StringWriter();
HtmlTextWriter html = new HtmlTextWriter(writer);

html.RenderBeginTag(HtmlTextWriterTag.H1);
html.WriteEncodedText("Heading Here");
html.RenderEndTag();
html.WriteEncodedText(String.Format("Dear {0}", userName));
html.WriteBreak();
html.RenderBeginTag(HtmlTextWriterTag.P);
html.WriteEncodedText("First part of the email body goes here");
html.RenderEndTag();
html.Flush();

string htmlString = writer.ToString();

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

В этом примере вы также можете довольно эффективно использовать XmlTextWriter: -

writer = new StringWriter();
XmlTextWriter xml = new XmlTextWriter(writer);
xml.Formatting = Formatting.Indented;
xml.WriteElementString("h1", "Heading Here");
xml.WriteString(String.Format("Dear {0}", userName));
xml.WriteStartElement("br");
xml.WriteEndElement();
xml.WriteElementString("p", "First part of the email body goes here");
xml.Flush();
ЭнтониУ Джонс
источник
2
Это кажется супер устаревшим
Дэниел
1
Для меня это был лучший ответ. Просто и по делу (хотя, надо признать, довольно неуклюже). MailDefinition был отличным, но не тем, что я искал.
thehelix
Привет, как добавить встроенный стиль, например, к h1тегу?
Иззи
когда всплывающее сообщение электронной почты было открыто, тело, я использую тег div, в моем контексте оно отображалось как <25>. Можете ли вы помочь с последним шагом, как сделать правильный рендеринг
пояснитель
17

Обновленный ответ :

В документации для SmtpClientкласса, использованного в этом ответе, теперь говорится: «Устаревший» («SmtpClient и его сеть типов плохо спроектированы, мы настоятельно рекомендуем вам использовать https://github.com/jstedfast/MailKit и https: // github .com / jstedfast / MimeKit вместо ")".

Источник: https://www.infoq.com/news/2017/04/MailKit-MimeKit-Official

Оригинальный ответ :

Использование класса MailDefinition - неправильный подход. Да, это удобно, но это также примитивно и зависит от элементов управления веб-интерфейсом - это не имеет смысла для того, что обычно является задачей на стороне сервера.

Представленный ниже подход основан на документации MSDN и публикации Куреши на CodeProject.com .

ПРИМЕЧАНИЕ. В этом примере файл HTML, изображения и вложения извлекаются из встроенных ресурсов, но можно использовать другие альтернативы для получения потоков для этих элементов, например жестко заданные строки, локальные файлы и т. Д.

Stream htmlStream = null;
Stream imageStream = null;
Stream fileStream = null;
try
{
    // Create the message.
    var from = new MailAddress(FROM_EMAIL, FROM_NAME);
    var to = new MailAddress(TO_EMAIL, TO_NAME);
    var msg = new MailMessage(from, to);
    msg.Subject = SUBJECT;
    msg.SubjectEncoding = Encoding.UTF8;
 
    // Get the HTML from an embedded resource.
    var assembly = Assembly.GetExecutingAssembly();
    htmlStream = assembly.GetManifestResourceStream(HTML_RESOURCE_PATH);
 
    // Perform replacements on the HTML file (if you're using it as a template).
    var reader = new StreamReader(htmlStream);
    var body = reader
        .ReadToEnd()
        .Replace("%TEMPLATE_TOKEN1%", TOKEN1_VALUE)
        .Replace("%TEMPLATE_TOKEN2%", TOKEN2_VALUE); // and so on...
 
    // Create an alternate view and add it to the email.
    var altView = AlternateView.CreateAlternateViewFromString(body, null, MediaTypeNames.Text.Html);
    msg.AlternateViews.Add(altView);
 
    // Get the image from an embedded resource. The <img> tag in the HTML is:
    //     <img src="pid:IMAGE.PNG">
    imageStream = assembly.GetManifestResourceStream(IMAGE_RESOURCE_PATH);
    var linkedImage = new LinkedResource(imageStream, "image/png");
    linkedImage.ContentId = "IMAGE.PNG";
    altView.LinkedResources.Add(linkedImage);
 
    // Get the attachment from an embedded resource.
    fileStream = assembly.GetManifestResourceStream(FILE_RESOURCE_PATH);
    var file = new Attachment(fileStream, MediaTypeNames.Application.Pdf);
    file.Name = "FILE.PDF";
    msg.Attachments.Add(file);
 
    // Send the email
    var client = new SmtpClient(...);
    client.Credentials = new NetworkCredential(...);
    client.Send(msg);
}
finally
{
    if (fileStream != null) fileStream.Dispose();
    if (imageStream != null) imageStream.Dispose();
    if (htmlStream != null) htmlStream.Dispose();
}
Сет
источник
9
Пожалуйста, не публикуйте ответ, который по сути является просто ссылкой на сообщение в вашем блоге. Если / когда ваш блог исчезнет, ​​ваш ответ станет практически бесполезным. Рассмотрите возможность включения «ключевых» частей сообщения в свой ответ здесь.
Роб
1
Содержание статьи в блоге перемещено сюда. Спасибо, @Rob.
Сет
1
FWIW этот код был протестирован и используется в производственном приложении.
Сет
1
Это немного сбивает с толку, поскольку в некоторых других библиотеках HTML отправляется как вложение. Я предлагаю удалить часть вложения PDF из этого образца, чтобы сделать его более понятным. Кроме того, msg.Body никогда не устанавливается в этом примере кода, я предполагаю, что ему следует присвоить переменную body?
Гудлаугур Эгильссон
1
Отсутствует ключевой шаг, установка которого msg.IsBodyHtml = true. См. Этот ответ здесь: stackoverflow.com/questions/7873155/…
Гудлаугур Эгильссон
6

Я использую dotLiquid именно для этой задачи.

Он берет шаблон и заполняет специальные идентификаторы содержимым анонимного объекта.

//define template
String templateSource = "<h1>{{Heading}}</h1>Dear {{UserName}},<br/><p>First part of the email body goes here");
Template bodyTemplate = Template.Parse(templateSource); // Parses and compiles the template source

//Create DTO for the renderer
var bodyDto = new {
    Heading = "Heading Here",
    UserName = userName
};
String bodyText = bodyTemplate.Render(Hash.FromAnonymousObject(bodyDto));

Он также работает с коллекциями, см. Некоторые онлайн-примеры .

завивать волосы щипцами
источник
может templateSource быть файлом .html? или еще лучше файл бритвы .cshtml?
ozzy432836
1
@Ozzy Это может быть любой (текстовый) файл. DotLiquid даже позволяет изменять синтаксис шаблона, если он мешает работе вашего файла шаблона.
Марсель
5

Я бы рекомендовал использовать какие-то шаблоны. Есть несколько разных способов подойти к этому, но по сути держать шаблон электронного письма где-нибудь (на диске, в базе данных и т. Д.) И просто вставлять ключевые данные (IE: имя получателя и т. Д.) В шаблон.

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

Натуральная кожа
источник
Согласен, все ответы в значительной степени делают одно и то же, используя разные методы. String.Format - это все, что вам нужно, и вы можете использовать любой из них для создания собственного шаблона.
user1040975
4

В качестве альтернативы MailDefinition взгляните на RazorEngine https://github.com/Antaris/RazorEngine .

Похоже, это лучшее решение.

Приписывается ...

как отправить электронное письмо с шаблоном электронной почты С #

Например

using RazorEngine;
using RazorEngine.Templating;
using System;

namespace RazorEngineTest
{
    class Program
    {
        static void Main(string[] args)
        {
    string template =
    @"<h1>Heading Here</h1>
Dear @Model.UserName,
<br />
<p>First part of the email body goes here</p>";

    const string templateKey = "tpl";

    // Better to compile once
    Engine.Razor.AddTemplate(templateKey, template);
    Engine.Razor.Compile(templateKey);

    // Run is quicker than compile and run
    string output = Engine.Razor.Run(
        templateKey, 
        model: new
        {
            UserName = "Fred"
        });

    Console.WriteLine(output);
        }
    }
}

Какие выходы ...

<h1>Heading Here</h1>
Dear Fred,
<br />
<p>First part of the email body goes here</p>

Направление сюда

Дорогой Фред,

Первая часть тела письма идет сюда

Мик
источник
это даст наибольший выбор, когда есть необходимость в циклах и если
CMS
3

Создание подобного HTML вручную, вероятно, лучший способ, если разметка не слишком сложна. Конструктор строк начинает окупаться с точки зрения эффективности только после трех конкатенаций, так что для действительно простых вещей подойдет строка + строка.

Помимо этого, вы можете начать использовать элементы управления html (System.Web.UI.HtmlControls) и отображать их, таким образом вы можете иногда наследовать их и создавать свои собственные классы для сложной условной компоновки.

Марк Дикинсон
источник
1

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

Саймон Фэрроу
источник
Просто к сведению - ссылка на URL в вашем ответе больше не актуальна; ведет на новостной сайт.
Джеовани Мартинес,
1

Если вам не нужна зависимость от полной .NET Framework, есть также библиотека, которая заставляет ваш код выглядеть так:

string userName = "John Doe";

var mailBody = new HTML {
    new H(1) {
        "Heading Here"
    },
    new P {
        string.Format("Dear {0},", userName),
        new Br()
    },
    new P {
        "First part of the email body goes here"
    }
};

string htmlString = mailBody.Render();

Это открытый исходный код, вы можете скачать его с http://sourceforge.net/projects/htmlplusplus/

Отказ от ответственности: я являюсь автором этой библиотеки, она была написана для решения той же проблемы - отправки электронного письма в формате HTML из приложения.

Владислав Зоров
источник
1

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

Contact templateData = ...; 
string html = Template
     .FromFile("template.txt")
     .DataFrom(templateData )
     .Render();

Стоит взглянуть, как и я; после попытки различных ответов, упомянутых здесь.

Джеовани Мартинес
источник