การสร้างเนื้อหาอีเมล 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 เนื่องจากเรารู้ว่าการป้อนข้อมูลของอีเมลล่วงหน้า
Cyberzed

ขึ้นอยู่กับความต้องการของคุณว่าซับซ้อนเพียงใด ฉันเคยมีแอปพลิเคชันที่แสดงตารางในอีเมล HTML และฉันใช้ ASP.NET Gridview เพื่อแสดงสตริงที่เชื่อมต่อ HTML เพื่อสร้างตารางจะยุ่ง
RichardOD

คำตอบ:


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 # โดยใช้แม่แบบโดยใช้ระดับ


11
ฉันไม่รู้ด้วยซ้ำว่าสิ่งนี้มีอยู่จริงอัจฉริยะที่แท้จริงคือ ... +1 และยอมรับ
Rob

5
+1 ดีแม้ว่าจะมีจำนวน จำกัด แต่อาจครอบคลุมการใช้งานหลายอย่าง ไม่มีประโยชน์หากคุณต้องการรวมส่วนของ HTML และ / หรือวนซ้ำชุดของรายการที่ต้องแสดงผลโดยทางโปรแกรม
AnthonyWJones

เพิ่งรู้ตัวเมื่อไม่นานนี้เอง มันเจ๋งมาก ฉันเดาว่ามันบอกคุณว่าการดูเอกสาร MSDN นั้นสำคัญแค่ไหนก่อนที่จะเขียนชั้นเรียนสำหรับปัญหาใด ๆ ด้วยตัวคุณเอง ฉันเขียนคลาสของตัวเองซึ่งเกือบจะเหมือนกับ MailDefinition เสียดายตังค์มาก เสียเวลา.
MartinHN

4
ฉันรู้สึกไม่สบายใจที่จะใช้คลาส MailDefinition เนื่องจากตัวเลือกที่ จำกัด สำหรับการระบุฟิลด์ from, to, cc และ bcc นอกจากนี้ยังอาศัยเนมสเปซสำหรับการควบคุม Web UI ซึ่งไม่สมเหตุสมผลสำหรับฉัน ดูคำตอบของฉันด้านล่าง ...
Seth

+1 เนื่องจากฉันไม่ทราบว่ามีคลาสนี้อยู่แม้ว่าการใช้งานพื้นฐานจะเป็นเพียงการวนซ้ำในรายการการแทนที่และทำงานRegex.Replace(body, pattern, replacement, RegexOptions.IgnoreCase);สำหรับคู่คีย์ / ค่าแต่ละคู่ ... ในแง่ของรายละเอียดนั้นคลาสนี้ไม่ได้ให้ค่ามากอย่างที่ใช้ข้างต้น System.Web.UI.Controlsเว้นแต่การทำงานกับการอ้างอิงที่มีอยู่เพื่อ
Chris Baxter

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 น่าจะเป็นวิธีที่ดีที่สุด อย่างไรก็ตามอาจใช้งานได้ค่อนข้างยากและนักพัฒนาบางคนชอบมาร์กอัปเองเพื่อให้อ่านได้ง่าย แต่ตัวเลือกของ HtmlTextWrit ในทางที่ผิดเกี่ยวกับการเยื้องนั้นค่อนข้างแย่

ในตัวอย่างนี้คุณยังสามารถใช้ 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
นี่ดูโบราณสุด ๆ
Daniel

1
นี่เป็นคำตอบที่ดีที่สุดสำหรับฉัน เรียบง่ายและตรงประเด็น (แม้ว่าจะดูไม่เป็นระเบียบ) MailDefinition เป็นสิ่งที่ดี แต่ไม่ใช่สิ่งที่ฉันกำลังมองหา
thehelix

สวัสดีเราจะเพิ่มสไตล์อินไลน์ให้กับh1แท็กได้อย่างไร
Izzy

เมื่อป๊อปอัปอีเมลถูกเปิดเนื้อหา iam ใช้แท็ก 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 เป็นแนวทางที่ไม่ถูกต้อง ใช่มันมีประโยชน์ แต่ก็เป็นแบบดั้งเดิมและขึ้นอยู่กับการควบคุม UI ของเว็บซึ่งไม่สมเหตุสมผลสำหรับบางสิ่งที่โดยทั่วไปแล้วจะเป็นงานฝั่งเซิร์ฟเวอร์

วิธีการดังต่อไปนี้จะขึ้นอยู่กับเอกสาร 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
โปรดอย่าโพสต์คำตอบที่เป็นเพียงลิงก์ไปยังบล็อกโพสต์ของคุณ ถ้า / เมื่อบล็อกของคุณหายไปคำตอบของคุณที่นี่จะไร้ประโยชน์มาก ลองรวมส่วน "สำคัญ" ของโพสต์ไว้ในคำตอบของคุณที่นี่
Rob

1
ย้ายเนื้อหาบทความบล็อกมาที่นี่ ขอบคุณ @Rob
Seth

1
FWIW รหัสนี้ได้รับการทดสอบและกำลังใช้ในแอปพลิเคชันการผลิต
Seth

1
สิ่งนี้ค่อนข้างสับสนเนื่องจากในไลบรารีอื่น ๆ HTML จะถูกส่งเป็นไฟล์แนบ ฉันขอแนะนำให้ลบส่วนไฟล์แนบ PDF ออกจากตัวอย่างนี้เพื่อให้ชัดเจนขึ้น นอกจากนี้ผงชูรสไม่เคยตั้งค่าในโค้ดตัวอย่างนี้ฉันคิดว่าควรกำหนดตัวแปร body?
Gudlaugur Egilsson

1
ไม่มีขั้นตอนสำคัญซึ่งการตั้งค่า msg.IsBodyHtml = true ดูคำตอบได้ที่นี่: stackoverflow.com/questions/7873155/…
Gudlaugur 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 ยังอนุญาตให้เปลี่ยนไวยากรณ์ของเทมเพลตในกรณีที่รบกวนไฟล์เทมเพลตของคุณ
Marcel

5

ฉันขอแนะนำให้ใช้เทมเพลตบางประเภท มีหลายวิธีในการดำเนินการนี้ แต่โดยพื้นฐานแล้วจะเก็บแม่แบบของอีเมลไว้ที่ใด (บนดิสก์ในฐานข้อมูล ฯลฯ ) และเพียงแค่ใส่ข้อมูลสำคัญ (IE: ชื่อผู้รับ ฯลฯ ) ลงในเทมเพลต

สิ่งนี้ยืดหยุ่นกว่ามากเพราะหมายความว่าคุณสามารถปรับเปลี่ยนเทมเพลตได้ตามต้องการโดยไม่ต้องแก้ไขโค้ดของคุณ จากประสบการณ์ของฉันคุณมีแนวโน้มที่จะได้รับคำขอเปลี่ยนแปลงเทมเพลตจากผู้ใช้ปลายทาง หากคุณต้องการใช้หมูทั้งหมดคุณสามารถรวมตัวแก้ไขเทมเพลต


เห็นด้วยคำตอบทั้งหมดทำในสิ่งเดียวกันโดยใช้วิธีการที่แตกต่างกัน String.Format คือสิ่งที่คุณต้องการและคุณสามารถใช้สิ่งเหล่านี้เพื่อสร้างเทมเพลตของคุณเอง
user1040975

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>

มุ่งหน้ามาที่นี่

เรียน Fred

ส่วนแรกของเนื้อหาอีเมลอยู่ที่นี่


สิ่งนี้จะให้ตัวเลือกมากที่สุดเมื่อจำเป็นต้องมีลูป & ifs
CMS

3

การปล่อย html ที่สร้างขึ้นด้วยมือเช่นนี้อาจเป็นวิธีที่ดีที่สุดตราบใดที่มาร์กอัปไม่ซับซ้อนเกินไป นักสร้างสตริงจะเริ่มจ่ายเงินคืนให้คุณในแง่ของประสิทธิภาพหลังจากการเรียงต่อกันประมาณสามครั้งดังนั้นสำหรับสตริง + สตริงที่ง่ายจริงๆจะทำ

นอกเหนือจากนั้นคุณสามารถเริ่มใช้ตัวควบคุม html (System.Web.UI.HtmlControls) และแสดงผลได้ด้วยวิธีนี้บางครั้งคุณสามารถสืบทอดและสร้างระดับของคุณเองสำหรับรูปแบบเงื่อนไขที่ซับซ้อน


1

คุณอาจต้องการดูเทมเพลตเฟรมเวิร์กบางส่วนที่พร้อมใช้งานในขณะนี้ บางส่วนเป็นผลจาก MVC แต่ไม่จำเป็น จุดประกายเป็นสิ่งที่ดี


เพียงแค่ FYI - การอ้างอิง URL ในคำตอบของคุณไม่เกี่ยวข้องอีกต่อไป นำไปสู่เว็บไซต์ข่าว
Geovani Martinez

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

คุ้มค่าที่จะดูเช่นฉัน; หลังจากลองใช้คำตอบต่างๆที่กล่าวถึงที่นี่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.