web-dev-qa-db-fra.com

Générer le corps d'un email HTML en C #

Existe-t-il un meilleur moyen de générer un courrier électronique HTML en C # (pour l'envoi via System.Net.Mail) par rapport à l'utilisation d'un constructeur de chaînes 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?

108
Rob

Vous pouvez utiliser le 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 procédure à suivre générer un corps de courrier électronique HTML en C # à l'aide de modèles utilisant la classe MailDefinition .

176
MartinHN

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 langage HTML étendu comprenant 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 soit facilement lisible, mais les choix pervers de HtmlTextWriter concernant 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();
25
AnthonyWJones

Réponse mise à jour :

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

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

Réponse originale :

L'utilisation de la classe MailDefinition est une mauvaise approche. Oui, c'est pratique, mais c'est aussi primitif et dépend des contrôles de l'interface utilisateur Web - cela n'a pas de sens pour une tâche 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 message de Qureshi sur CodeProject.com .

REMARQUE: Cet exemple extrait le fichier HTML, les images et les pièces jointes à partir de ressources incorporées, mais l'utilisation d'autres alternatives pour obtenir des flux pour ces éléments est correcte, par exemple. chaînes codées en dur, 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();
}
15
Seth

J'utilise dotLiquid pour exactement cette tâche.

Il prend un modèle et remplit des identifiants 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));

Cela fonctionne aussi avec les collections, voir quelques exemples en ligne .

4
Marcel

Je recommanderais d'utiliser des modèles de quelque sorte. Il existe différentes manières d’aborder cette question, mais il est essentiel de conserver un modèle de courrier électronique quelque part (sur disque, dans une base de données, etc.) et d’insérer simplement les données de clé (IE: nom du destinataire, etc.) dans le modèle.

Ceci est beaucoup plus flexible car cela signifie que vous pouvez modifier le modèle à votre guise 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 tout faire, vous pouvez inclure un éditeur de modèles.

4
Leather

Au lieu de MailDefinition, consultez RazorEngine https://github.com/Antaris/RazorEngine .

Cela ressemble à une meilleure solution.

Attributté à ...

comment envoyer un email avec email modèle 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>

En route vers ici

Cher Fred,

La première partie du corps de l'email va ici

3
Mick

Émettre du code HTML à la main comme celui-ci est probablement la meilleure solution 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;.

En dehors de cela, vous pouvez commencer à utiliser les contrôles html (System.Web.UI.HtmlControls) et les restituer, ce qui vous permet parfois de les hériter et de créer votre propre classe pour une mise en page conditionnelle complexe.

3
Mark Dickinson

Vous voudrez peut-être jeter un coup d'œil à certains des modèles de cadres disponibles à ce jour. Certains d'entre eux sont dérivés de MVC, mais ce n'est pas obligatoire. Spark est un bon.

2
Simon Farrow

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

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 depuis http://sourceforge.net/projects/htmlplusplus/

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

1
Vladislav Zorov

Il existe une question de StackOverflow similaire qui contient des réponses assez complètes. Personnellement, j'utilise NVelocity comme moteur de gabarit après avoir précédemment essayé d’utiliser le moteur ASP.Net pour générer du contenu e-mail en HTML. NVelocity est beaucoup plus simple à utiliser tout en offrant une grande flexibilité. J'ai constaté que l'utilisation de fichiers ASP.Net . Aspx pour les modèles fonctionnait mais avait des effets secondaires inattendus.

0
David Clarke