Generazione del corpo dell'email HTML in C #


114

Esiste un modo migliore per generare messaggi di posta elettronica HTML in C # (per l'invio tramite System.Net.Mail), rispetto all'utilizzo di un Stringbuilder per eseguire le seguenti operazioni:

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

E così via e così via?


Beh, dipende davvero dalla soluzione come la vedo io. Ho fatto di tutto, dall'acquisizione dell'input dell'utente e alla formattazione automatica da diversi schemi. La migliore soluzione che ho fatto con le mail html è stata in realtà la formattazione xml + xslt poiché conoscevamo l'input della posta in anticipo.
cyberzato

Dipende dalla complessità delle tue esigenze. Una volta avevo un'applicazione che eseguiva il rendering di una tabella in un messaggio di posta elettronica HTML e utilizzavo un ASP.NET Gridview per eseguire il rendering delle stringhe di concatenazione HTML per generare una tabella sarebbe stato disordinato.
RichardOD

Risposte:


181

Puoi usare il file classe MailDefinition .

Ecco come lo usi:

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

Inoltre, ho scritto un post sul blog su come generare il corpo del messaggio di posta elettronica HTML in C # utilizzando i modelli utilizzando la classe MailDefinition .


11
Non sapevo nemmeno che esistesse, puro genio è ... +1 e accettato
Rob

5
+1. Bello, sebbene limitato, probabilmente copre molti usi. Non così utile se si desidera includere a livello di codice sezioni di HTML e / o eseguire il ciclo attraverso un set di elementi che richiedono il rendering.
AnthonyWJones

Ne sono venuto a conoscenza solo di recente. È fresco. Immagino che ti dica quanto sia importante guardare la documentazione di MSDN prima di scrivere un corso per qualsiasi problema. Avevo scritto la mia classe, che ha fatto quasi la stessa cosa di MailDefinition. Peccato per me. Perdita di tempo.
MartinHN

4
Non mi sentivo a mio agio nell'usare la classe MailDefinition a causa delle opzioni limitate per specificare i campi da, a, cc e bcc. Si basa anche su uno spazio dei nomi per i controlli dell'interfaccia utente Web, che non ha senso per me. Vedi la mia risposta qui sotto ...
Seth

+1 poiché non sapevo che questa classe esistesse, sebbene l'implementazione sottostante stia semplicemente iterando sull'elenco delle sostituzioni e in esecuzione Regex.Replace(body, pattern, replacement, RegexOptions.IgnoreCase);per ogni coppia chiave / valore ... alla luce di questi dettagli, questa classe non fornisce molto valore come usato sopra a meno che non si lavori con un riferimento esistente a System.Web.UI.Controls.
Chris Baxter

27

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

Per HTML estensivo che include la creazione di attributi di stile, HtmlTextWriter è probabilmente il modo migliore per procedere. Tuttavia può essere un po 'goffo da usare e ad alcuni sviluppatori piace il markup stesso per essere letto facilmente, ma le scelte perversamente di HtmlTextWriter riguardo all'indentazione sono un po' strane.

In questo esempio puoi anche usare XmlTextWriter in modo abbastanza efficace: -

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
Sembra super antiquato
Daniel

1
Questa è stata la risposta migliore per me. Semplice e al punto (anche se devo ammettere che piuttosto goffo). MailDefinition era elegante, ma non quello che stavo cercando.
thehelix

Ciao, come aggiungere uno stile in linea, ad esempio, al h1tag?
Izzy

quando è stato aperto il popup dell'e-mail, il corpo, iam utilizzando il tag div nel mio contesto veniva visualizzato come <25>. Puoi aiutarci con l'ultimo passaggio su come eseguire un rendering corretto
chiarimento

17

Risposta aggiornata :

La documentazione per SmtpClient, la classe utilizzata in questa risposta, ora legge, "Obsolete (" SmtpClient e la sua rete di tipi sono mal progettati, ti consigliamo vivamente di utilizzare https://github.com/jstedfast/MailKit e https: // github .com / jstedfast / MimeKit invece ") '.

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

Risposta originale :

L'utilizzo della classe MailDefinition è l'approccio sbagliato. Sì, è utile, ma è anche primitivo e dipende dai controlli dell'interfaccia utente Web: non ha senso per qualcosa che è in genere un'attività lato server.

L'approccio presentato di seguito si basa sulla documentazione MSDN e sul post di Qureshi su CodeProject.com .

NOTA: questo esempio estrae il file HTML, le immagini e gli allegati dalle risorse incorporate, ma l'uso di altre alternative per ottenere flussi per questi elementi va bene, ad esempio stringhe hard-coded, file locali e così via.

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
Per favore, non pubblicare una risposta che sia essenzialmente solo un link al tuo post sul blog. Se / quando il tuo blog scompare, la tua risposta qui diventa praticamente inutile. Considera l'idea di incorporare le parti "chiave" del post nella tua risposta qui.
Rob

1
Contenuto dell'articolo del blog spostato qui. Grazie, @Rob.
Seth

1
FWIW questo codice è stato testato e viene utilizzato in un'applicazione di produzione.
Seth

1
Questo crea un po 'di confusione, poiché in alcune altre librerie l'HTML viene inviato come allegato. Suggerisco di rimuovere la porzione di allegato PDF da questo esempio per renderlo più chiaro. Inoltre, msg.Body non è mai impostato in questo codice di esempio, presumo che dovrebbe essere assegnata la variabile body?
Gudlaugur Egilsson

1
Manca un passaggio chiave, l'impostazione di msg.IsBodyHtml = true. Vedere questa risposta qui: stackoverflow.com/questions/7873155/...
Gudlaugur Egilsson

6

Uso dotLiquid esattamente per questo compito.

Prende un modello e riempie identificatori speciali con il contenuto di un oggetto anonimo.

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

Funziona anche con le raccolte, guarda alcuni esempi online .


templateSource può essere un file .html? o meglio ancora un file razor .cshtml?
ozzy432836

1
@Ozzy Può effettivamente essere qualsiasi file (di testo). DotLiquid consente anche di modificare la sintassi del modello nel caso in cui interferisca con il file del modello.
Marcel

5

Consiglierei di utilizzare modelli di qualche tipo. Ci sono vari modi diversi per affrontare questo problema, ma essenzialmente tieni un modello dell'E-mail da qualche parte (su disco, in un database, ecc.) E inserisci semplicemente i dati chiave (IE: nome dei destinatari ecc.) Nel modello.

Questo è molto più flessibile perché significa che puoi modificare il modello come richiesto senza dover modificare il tuo codice. Nella mia esperienza è probabile che tu riceva richieste di modifica ai modelli da parte degli utenti finali. Se vuoi andare fino in fondo potresti includere un editor di modelli.


D'accordo, tutte le risposte fanno praticamente la stessa cosa usando metodi diversi. String.Format è tutto ciò di cui hai bisogno e puoi utilizzare uno qualsiasi di questi per creare il tuo modello.
user1040975

4

In alternativa a MailDefinition, dai un'occhiata a RazorEngine https://github.com/Antaris/RazorEngine .

Questa sembra una soluzione migliore.

Attribuito a ...

come inviare e-mail con il modello di e-mail c #

Per esempio

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

Quali uscite ...

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

Dirigendosi qui

Caro Fred,

La prima parte del corpo dell'email va qui


questo darà la maggior parte delle opzioni quando c'è bisogno di loop e ifs
CMS

3

Emettere html costruito a mano in questo modo è probabilmente il modo migliore fintanto che il markup non è troppo complicato. Il costruttore di stringhe inizia a ripagarti in termini di efficienza solo dopo circa tre concatenazioni, quindi per cose davvero semplici stringa + stringa andrà bene.

Oltre a questo puoi iniziare a utilizzare i controlli html (System.Web.UI.HtmlControls) e renderli, in questo modo a volte puoi ereditarli e creare la tua classificazione per layout condizionale complesso.


1

Potresti voler dare un'occhiata ad alcuni dei framework di modelli che sono disponibili al momento. Alcuni di loro sono spin off come risultato di MVC ma non è necessario. Spark è buono.


Solo un FYI - Il riferimento all'URL nella tua risposta non è più rilevante; porta a un sito web di notizie.
Geovani Martinez

1

Se non vuoi una dipendenza dal .NET Framework completo, c'è anche una libreria che fa sembrare il tuo codice:

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

È open source, puoi scaricarlo da http://sourceforge.net/projects/htmlplusplus/

Dichiarazione di non responsabilità: sono l'autore di questa libreria, è stata scritta per risolvere esattamente lo stesso problema: inviare un'e-mail HTML da un'applicazione.


1

Una versione commerciale che utilizzo in produzione e che consente una facile manutenzione è LimiLabs Template Engine , lo uso da più di 3 anni e mi permette di apportare modifiche al modello di testo senza dover aggiornare il codice (disclaimer, link, ecc.) - it potrebbe essere semplice come

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

Vale la pena dare un'occhiata, come ho fatto io; dopo aver tentato varie risposte qui menzionate.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.