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

Це залежить від того, наскільки складні ваші вимоги. Колись у мене була програма, яка рендерувала таблицю в HTML-адресу електронної пошти, і я використовував ASP.NET Gridview, щоб зробити рядки, що поєднують HTML, для створення таблиці було б безладним.
РічардОД

Відповіді:


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 .


11
Я навіть не знав, що це існує, абсолютний геній, це ... +1 і прийнято
Роб

5
+1. Хороший, хоча і обмежений, мабуть, охоплює багато застосувань. Не так корисно, якщо ви хочете програмно включати розділи HTML та / або циклу через набір елементів, які потребують візуалізації.
AnthonyWJones

Про це я дізнався зовсім недавно. Це круто. Я думаю, це говорить вам про те, як важливо переглянути документацію MSDN перед тим, як самостійно написати клас для будь-якої проблеми. Я написав власний клас, який зробив майже те саме, що і MailDefinition. Шкода для мене. Марна трата часу.
MartinHN

4
Мені було незручно користуватися класом MailDefinition через обмежені можливості визначення полів від, до, куб.см і 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 та публікації Qureshi на 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 ніколи не встановлений у цьому зразковому коді, я припускаю, що йому слід призначити змінну тіла?
Гудлаугур Егільссон

1
Відсутній ключовий крок, який встановлює msg.IsBodyHtml = true. Дивіться цей відповідь тут: stackoverflow.com/questions/7873155 / ...
Гудлаугур Egilsson

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

4

Як альтернативу MailDefinition, подивіться на RazorEngine https://github.com/Antaris/RazorEngine .

Це виглядає як краще рішення.

Додано до ...

як надіслати електронний лист із шаблоном електронної пошти c #

Напр

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>

Заголовок сюди

Шановний Фреде,

Перша частина електронної пошти йде тут


це дасть найбільш оптимальний варіант, коли виникає потреба в циклі і ifs
CMS

3

Випускати вручну вбудований HTML, як це, мабуть, найкращий спосіб, поки розмітка не є надто складною. Строкобудівник тільки починає відшкодовувати вас з точки зору ефективності після приблизно трьох конкатенацій, тому для дійсно простих речей string + string буде робити.

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


1

Можливо, ви хочете ознайомитись із деякими шаблонами рамок, доступними на даний момент. Деякі з них відкручуються в результаті MVC, але це не потрібно. Іскра хороша.


Просто посилання у вашій відповіді FYI - 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 Template Engine , яка використовує її протягом 3+ років і дозволяє мені вносити зміни в текстовий шаблон без необхідності оновлення коду (відмови від відповідальності, посилання тощо) - це може бути таким же простим, як

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

Варто поглянути, як і я; після спроб різних відповідей, згаданих тут.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.