Generieren eines HTML-E-Mail-Textes in C #

113

Gibt es eine bessere Möglichkeit, HTML-E-Mails in C # zu generieren (zum Senden über System.Net.Mail), als einen Stringbuilder zu verwenden, um Folgendes zu tun:

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

und so weiter und so fort?

rauben
quelle
Nun, es hängt wirklich von der Lösung ab, wie ich sie sehe. Ich habe alles getan, angefangen beim Abrufen von Benutzereingaben bis hin zum automatischen Formatieren aus verschiedenen Mustern. Die beste Lösung, die ich mit HTML-Mails gemacht habe, war die Formatierung von xml + xslt, da wir die Eingabe der Mail im Voraus kannten.
Cyberzed
Das hängt davon ab, wie komplex Ihre Anforderungen sind. Ich hatte einmal eine Anwendung, die eine Tabelle in einer HTML-E-Mail gerendert hat, und ich habe eine ASP.NET-Rasteransicht verwendet, um die HTML-verkettenden Zeichenfolgen zum Generieren einer Tabelle zu rendern. Das wäre chaotisch gewesen.
RichardOD

Antworten:

180

Sie können die MailDefinition-Klasse verwenden .

So verwenden Sie es:

MailDefinition md = new MailDefinition();
md.From = "[email protected]";
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("[email protected]", replacements, body, new System.Web.UI.Control());

Außerdem habe ich einen Blog-Beitrag darüber geschrieben, wie HTML-E-Mail-Text in C # mithilfe von Vorlagen mithilfe der MailDefinition-Klasse generiert wird .

MartinHN
quelle
11
Ich wusste nicht einmal, dass es das gibt, schier genial ist es ... +1 und akzeptiert
Rob
5
+1. Nizza, obwohl begrenzt, deckt wahrscheinlich viele Verwendungszwecke ab. Nicht so nützlich, wenn Sie Abschnitte von HTML programmgesteuert einschließen und / oder eine Reihe von Elementen durchlaufen möchten, die gerendert werden müssen.
AnthonyWJones
Ich bin erst kürzlich darauf aufmerksam geworden. Es ist cool. Ich denke, es sagt Ihnen, wie wichtig es ist, die MSDN-Dokumentation zu lesen, bevor Sie selbst eine Klasse für ein Problem schreiben. Ich hatte meine eigene Klasse geschrieben, die fast das gleiche tat wie MailDefinition. Schade für mich. Zeitverschwendung.
MartinHN
4
Die Verwendung der MailDefinition-Klasse war mir aufgrund der eingeschränkten Optionen zum Angeben der Felder from, to, cc und bcc unangenehm. Es basiert auch auf einem Namespace für Web-UI-Steuerelemente - das macht für mich keinen Sinn. Siehe meine Antwort unten ...
Seth
+1, da ich nicht wusste, dass diese Klasse existiert, obwohl die zugrunde liegende Implementierung einfach die Ersetzungsliste durchläuft und Regex.Replace(body, pattern, replacement, RegexOptions.IgnoreCase);für jedes Schlüssel / Wert-Paar ausgeführt wird ... Angesichts dieses Details bietet diese Klasse nicht viel Wert, wie oben verwendet es sei denn, Sie arbeiten mit einem vorhandenen Verweis auf System.Web.UI.Controls.
Chris Baxter
27

Verwenden Sie die System.Web.UI.HtmlTextWriter-Klasse.

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

Für umfangreiches HTML, das die Erstellung von Stilattributen umfasst, ist HtmlTextWriter wahrscheinlich der beste Weg. Die Verwendung kann jedoch etwas umständlich sein, und einige Entwickler möchten, dass das Markup selbst leicht zu lesen ist, aber pervers ist die Auswahl von HtmlTextWriter in Bezug auf Einrückungen etwas seltsam.

In diesem Beispiel können Sie XmlTextWriter auch sehr effektiv verwenden: -

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();
AnthonyWJones
quelle
2
Dies scheint super antiquiert
Daniel
1
Dies war die beste Antwort für mich. Einfach und auf den Punkt (obwohl zugegebenermaßen ziemlich klobig). MailDefinition war geschickt, aber nicht das, wonach ich gesucht habe.
Thehelix
Hallo, wie würde man zum Beispiel dem h1Tag Inline-Styling hinzufügen ?
Izzy
wenn die E - Mail geöffnet wurde Pop - up, den Körper, iam div - Tag in meinem Zusammenhang mit ihm als <25> Rendering .can u Hilfe bei der letzten Schritt bitte , wie man ein richtige Rendering zu machen
Klärbecken
16

Aktualisierte Antwort :

Die Dokumentation für SmtpClientdie in dieser Antwort verwendete Klasse lautet nun: 'Veraltet ("SmtpClient und sein Netzwerk von Typen sind schlecht gestaltet. Wir empfehlen dringend, https://github.com/jstedfast/MailKit und https: // github zu verwenden .com / jstedfast / MimeKit stattdessen ") '.

Quelle: https://www.infoq.com/news/2017/04/MailKit-MimeKit-Official

Ursprüngliche Antwort :

Die Verwendung der MailDefinition-Klasse ist der falsche Ansatz. Ja, es ist praktisch, aber es ist auch primitiv und hängt von den Steuerelementen der Web-Benutzeroberfläche ab. Dies ist für etwas, das normalerweise eine serverseitige Aufgabe ist, nicht sinnvoll.

Der unten dargestellte Ansatz basiert auf der MSDN-Dokumentation und dem Beitrag von Qureshi auf CodeProject.com .

ANMERKUNG: In diesem Beispiel werden HTML-Dateien, Bilder und Anhänge aus eingebetteten Ressourcen extrahiert. Die Verwendung anderer Alternativen zum Abrufen von Streams für diese Elemente ist jedoch in Ordnung, z. B. fest codierte Zeichenfolgen, lokale Dateien usw.

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();
}
Seth
quelle
9
Bitte poste keine Antwort, die im Wesentlichen nur ein Link zu deinem Blog-Beitrag ist. Wenn Ihr Blog verschwindet, wird Ihre Antwort hier so gut wie nutzlos. Erwägen Sie, die "Schlüssel" -Teile des Beitrags in Ihre Antwort hier aufzunehmen.
Rob
1
Der Inhalt des Blog-Artikels wurde hier verschoben. Danke, @Rob.
Seth
1
FWIW Dieser Code wurde getestet und wird in einer Produktionsanwendung verwendet.
Seth
1
Dies ist etwas verwirrend, da in einigen anderen Bibliotheken HTML als Anhang gesendet wird. Ich schlage vor, den PDF-Anhang aus diesem Beispiel zu entfernen, um es klarer zu machen. Außerdem wird der msg.Body in diesem Beispielcode nie gesetzt. Ich gehe davon aus, dass ihm die Body-Variable zugewiesen werden sollte.
Gudlaugur Egilsson
1
Es fehlt ein wichtiger Schritt, der msg.IsBodyHtml = true setzt. Sehen Sie diese Antwort hier: stackoverflow.com/questions/7873155/…
Gudlaugur Egilsson
6

Ich benutze dotLiquid für genau diese Aufgabe.

Es nimmt eine Vorlage und füllt spezielle Bezeichner mit dem Inhalt eines anonymen Objekts.

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

Es funktioniert auch mit Sammlungen, siehe einige Online-Beispiele .

Marcel
quelle
Kann templateSource eine HTML-Datei sein? oder noch besser eine .cshtml Rasiermesserdatei?
Ozzy432836
1
@ Ozzy Es kann tatsächlich jede (Text-) Datei sein. Mit DotLiquid können Sie sogar die Vorlagensyntax ändern, falls Ihre Vorlagendatei beeinträchtigt wird.
Marcel
5

Ich würde empfehlen, Vorlagen zu verwenden. Es gibt verschiedene Möglichkeiten, dies zu erreichen, aber im Wesentlichen eine Vorlage der E-Mail an einer bestimmten Stelle (auf der Festplatte, in einer Datenbank usw.) zu halten und einfach die Schlüsseldaten (IE: Name des Empfängers usw.) in die Vorlage einzufügen.

Dies ist weitaus flexibler, da Sie die Vorlage nach Bedarf ändern können, ohne Ihren Code ändern zu müssen. Nach meiner Erfahrung erhalten Sie wahrscheinlich Anfragen nach Änderungen an den Vorlagen von Endbenutzern. Wenn Sie das ganze Schwein gehen möchten, können Sie einen Vorlageneditor hinzufügen.

Leder
quelle
Einverstanden, Alle Antworten machen so ziemlich dasselbe mit unterschiedlichen Methoden. String.Format ist alles, was Sie brauchen, und Sie können jede dieser Vorlagen verwenden, um Ihre eigene Vorlage zu erstellen.
user1040975
4

Schauen Sie sich als Alternative zu MailDefinition RazorEngine https://github.com/Antaris/RazorEngine an .

Dies scheint eine bessere Lösung zu sein.

Zugeschrieben an ...

wie man eine E-Mail mit einer E-Mail-Vorlage sendet c #

Z.B

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

Welche Ausgänge ...

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

Auf dem Weg hierher

Lieber Fred,

Der erste Teil des E-Mail-Textes geht hier

Mick
quelle
Dies bietet die meisten Optionen, wenn Schleifen und Ifs erforderlich sind
CMS
3

Das Ausgeben von handgefertigtem HTML wie diesem ist wahrscheinlich der beste Weg, solange das Markup nicht zu kompliziert ist. Der Stringbuilder zahlt sich erst nach etwa drei Verkettungen in Bezug auf die Effizienz aus, sodass für wirklich einfache Dinge String + String ausreichen.

Ansonsten können Sie die HTML-Steuerelemente (System.Web.UI.HtmlControls) verwenden und rendern. Auf diese Weise können Sie sie manchmal erben und Ihre eigene Klasse für ein komplexes bedingtes Layout erstellen.

Mark Dickinson
quelle
1

Vielleicht möchten Sie einen Blick auf einige der derzeit verfügbaren Vorlagen-Frameworks werfen. Einige von ihnen sind Spin-offs aufgrund von MVC, aber das ist nicht erforderlich. Spark ist gut.

Simon Farrow
quelle
Nur eine FYI-URL-Referenz in Ihrer Antwort ist nicht mehr relevant. führt zu einer Nachrichten-Website.
Geovani Martinez
1

Wenn Sie keine Abhängigkeit vom vollständigen .NET Framework wünschen, gibt es auch eine Bibliothek, mit der Ihr Code wie folgt aussieht:

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

Es ist Open Source und kann von http://sourceforge.net/projects/htmlplusplus/ heruntergeladen werden.

Haftungsausschluss: Ich bin der Autor dieser Bibliothek. Sie wurde geschrieben, um das gleiche Problem genau zu lösen - senden Sie eine HTML-E-Mail von einer Anwendung.

Vladislav Zorov
quelle
1

Eine kommerzielle Version, die ich in der Produktion verwende und die eine einfache Wartung ermöglicht, ist die LimiLabs Template Engine , die sie seit mehr als 3 Jahren verwendet und es mir ermöglicht, Änderungen an der Textvorlage vorzunehmen, ohne den Code aktualisieren zu müssen (Haftungsausschlüsse, Links usw.) könnte so einfach sein wie

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

Es lohnt sich, einen Blick darauf zu werfen, wie ich es getan habe; nach dem Versuch verschiedene Antworten hier erwähnt.

Geovani Martinez
quelle