Générer le corps d'un e-mail HTML en C #

114

Existe-t-il un meilleur moyen de générer un e-mail HTML en C # (pour l'envoi via System.Net.Mail), que d'utiliser un Stringbuilder pour effectuer les opérations suivantes:

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

et ainsi de suite?

Rob
la source
Eh bien, cela dépend vraiment de la solution telle que je la vois. J'ai tout fait, depuis la saisie des entrées utilisateur et leur formatage automatique à partir de différents modèles. La meilleure solution que j'ai faite avec les mails html était en fait le formatage xml + xslt puisque nous connaissions l'entrée du mail dès le départ.
cyberzed
Cela dépend de la complexité de vos exigences. Une fois, j'avais une application qui rendait un tableau dans un e-mail HTML et j'utilisais un Gridview ASP.NET pour rendre les chaînes de concaténation HTML pour générer un tableau aurait été compliqué.
RichardOD

Réponses:

181

Vous pouvez utiliser la classe MailDefinition .

Voici comment vous l'utilisez:

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

En outre, j'ai écrit un article de blog sur la façon de générer un corps de courrier électronique HTML en C # à l'aide de modèles à l'aide de la classe MailDefinition .

MartinHN
la source
11
Je ne savais même pas que cela existait, pur génie c'est ... +1 et accepté
Rob
5
+1. Nice, bien que limité, couvre probablement de nombreux usages. Pas si utile si vous souhaitez inclure par programme des sections de HTML et / ou parcourir un ensemble d'éléments nécessitant un rendu.
AnthonyWJones
J'en ai pris conscience récemment. C'est cool. Je suppose que cela vous indique à quel point il est important de consulter la documentation MSDN avant d'écrire vous-même une classe pour tout problème. J'avais écrit ma propre classe, qui faisait presque la même chose que MailDefinition. Tant pis pour moi. Perte de temps.
MartinHN
4
J'étais mal à l'aise avec la classe MailDefinition en raison des options limitées pour spécifier les champs from, to, cc et bcc. Il repose également sur un espace de noms pour les contrôles de l'interface utilisateur Web - cela n'a aucun sens pour moi. Voir ma réponse ci-dessous ...
Seth
+1 car je ne savais pas que cette classe existait, bien que l'implémentation sous-jacente itère simplement sur la liste des remplacements et s'exécute Regex.Replace(body, pattern, replacement, RegexOptions.IgnoreCase);pour chaque paire clé / valeur ... à la lumière de ce détail, cette classe ne fournit pas beaucoup de valeur comme utilisé ci-dessus à moins de travailler avec une référence existante à System.Web.UI.Controls.
Chris Baxter
27

Utilisez la classe 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();

Pour un HTML étendu qui inclut la création d'attributs de style, HtmlTextWriter est probablement la meilleure solution. Cependant, il peut être un peu maladroit à utiliser et certains développeurs aiment que le balisage lui-même soit facilement lisible, mais les choix de HtmlTextWriter en ce qui concerne l'indentation sont un peu bizarres.

Dans cet exemple, vous pouvez également utiliser XmlTextWriter assez efficacement: -

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
la source
2
Cela semble super désuet
Daniel
1
C'était la meilleure réponse pour moi. Simple et précis (bien que certes assez maladroit). MailDefinition était chouette, mais pas ce que je recherchais.
thehelix
Salut, comment ajouterait-on un style en ligne, par exemple à la h1balise?
Izzy
lorsque la fenêtre contextuelle de l'e-mail a été ouverte, le corps, iam utilisant la balise div dans mon contexte, il était rendu comme <25> .Pouvez-vous aider à la dernière étape comment faire un rendu correct
clarificateur
17

Réponse mise à jour :

La documentation de SmtpClient, la classe utilisée dans cette réponse, se lit maintenant, 'Obsolète ("SmtpClient et son réseau de types sont mal conçus, nous vous recommandons fortement d'utiliser https://github.com/jstedfast/MailKit et https: // github .com / jstedfast / MimeKit à la place ") '.

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

Réponse originale :

L'utilisation de la classe MailDefinition n'est pas la bonne approche. Oui, c'est pratique, mais c'est aussi primitif et dépend des contrôles de l'interface utilisateur Web - cela n'a aucun sens pour quelque chose qui est généralement une tâche côté serveur.

L'approche présentée ci-dessous est basée sur la documentation MSDN et la publication de Qureshi sur CodeProject.com .

REMARQUE: cet exemple extrait le fichier HTML, les images et les pièces jointes des ressources intégrées, mais l'utilisation d'autres alternatives pour obtenir des flux pour ces éléments convient, par exemple des chaînes codées en dur, des fichiers locaux, etc.

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
la source
9
Veuillez ne pas publier une réponse qui n'est essentiellement qu'un lien vers votre article de blog. Si / quand votre blog disparaît, votre réponse ici devient pratiquement inutile. Pensez à incorporer les parties «clés» du message dans votre réponse ici.
Rob
1
Contenu de l'article de blog déplacé ici. Merci, @Rob.
Seth
1
FWIW, ce code a été testé et est utilisé dans une application de production.
Seth
1
C'est un peu déroutant, car dans certaines autres bibliothèques, le HTML est envoyé en pièce jointe. Je suggère de supprimer la partie pièce jointe PDF de cet exemple pour le rendre plus clair. En outre, le msg.Body n'est jamais défini dans cet exemple de code, je suppose qu'il devrait être affecté à la variable body?
Gudlaugur Egilsson
1
Il manque une étape clé, qui définit msg.IsBodyHtml = true. Voir cette réponse ici: stackoverflow.com/questions/7873155/…
Gudlaugur Egilsson
6

J'utilise dotLiquid exactement pour cette tâche.

Il prend un modèle et remplit les identificateurs spéciaux avec le contenu d'un objet anonyme.

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

Il fonctionne également avec des collections, voir quelques exemples en ligne .

Marcel
la source
templateSource peut-il être un fichier .html? ou mieux encore un fichier de rasoir .cshtml?
ozzy432836
1
@Ozzy Il peut en fait s'agir de n'importe quel fichier (texte). DotLiquid permet même de changer la syntaxe du modèle en cas d'interférence avec votre fichier modèle.
Marcel
5

Je recommanderais d'utiliser des modèles de quelque sorte. Il existe différentes façons d'aborder cela, mais il faut essentiellement conserver un modèle de courrier électronique quelque part (sur le disque, dans une base de données, etc.) et insérer simplement les données clés (IE: nom du destinataire, etc.) dans le modèle.

C'est beaucoup plus flexible car cela signifie que vous pouvez modifier le modèle selon vos besoins sans avoir à modifier votre code. D'après mon expérience, vous êtes susceptible de recevoir des demandes de modification des modèles de la part des utilisateurs finaux. Si vous voulez aller jusqu'au bout, vous pouvez inclure un éditeur de modèles.

Cuir
la source
D'accord, toutes les réponses font à peu près la même chose en utilisant des méthodes différentes. String.Format est tout ce dont vous avez besoin et vous pouvez utiliser l'un de ceux-ci pour créer votre propre modèle.
user1040975
4

Comme alternative à MailDefinition, jetez un œil à RazorEngine https://github.com/Antaris/RazorEngine .

Cela semble être une meilleure solution.

Attribué à ...

comment envoyer un e-mail avec le modèle d'e-mail c #

Par exemple

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

Quelles sorties ...

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

Cap ici

Cher Fred,

La première partie du corps de l'e-mail se trouve ici

Mick
la source
cela donnera le plus d'options quand il y aura un besoin de boucles et ifs
CMS
3

Émettre du code HTML construit à la main comme celui-ci est probablement le meilleur moyen tant que le balisage n'est pas trop compliqué. Le constructeur de chaînes ne commence à vous rembourser en termes d'efficacité qu'après environ trois concaténations, donc pour des choses vraiment simples, chaîne + chaîne fera l'affaire.

En dehors de cela, vous pouvez commencer à utiliser les contrôles html (System.Web.UI.HtmlControls) et à les rendre, de cette façon vous pouvez parfois les hériter et créer votre propre clasess pour une mise en page conditionnelle complexe.

Mark Dickinson
la source
1

Vous voudrez peut-être jeter un coup d'œil à certains des cadres de modèles disponibles pour le moment. Certains d'entre eux sont des spin-offs à la suite de MVC, mais ce n'est pas obligatoire. Spark en est une bonne.

Simon Farrow
la source
Juste un FYI - La référence URL dans votre réponse n'est plus pertinente; mène à un site Web d'actualités.
Geovani Martinez le
1

Si vous ne voulez pas de dépendance sur le .NET Framework complet, il existe également une bibliothèque qui donne à votre code l'aspect:

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

C'est open source, vous pouvez le télécharger sur http://sourceforge.net/projects/htmlplusplus/

Avertissement: je suis l'auteur de cette bibliothèque, elle a été écrite pour résoudre exactement le même problème - envoyer un e-mail HTML à partir d'une application.

Vladislav Zorov
la source
1

Une version commerciale que j'utilise en production et permet une maintenance facile est LimiLabs Template Engine , qui l'utilise depuis plus de 3 ans et me permet d'apporter des modifications au modèle de texte sans avoir à mettre à jour le code (avertissements, liens, etc.) - il pourrait être aussi simple que

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

Ça vaut le coup d'y jeter un œil, comme je l'ai fait; après avoir tenté diverses réponses mentionnées ici.

Geovani Martinez
la source