C #에서 HTML 이메일 본문 생성


114

Stringbuilder를 사용하여 다음을 수행하는 것보다 C # (System.Net.Mail을 통해 보내기)에서 HTML 이메일을 생성하는 더 좋은 방법이 있습니까?

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

또한 MailDefinition 클래스를 사용하여 템플릿을 사용하여 C #에서 HTML 전자 메일 본문 을 생성 하는 방법에 대한 블로그 게시물을 작성했습니다 .


11
나는 이것이 존재한다는 것을 몰랐습니다. 그것은 천재적입니다. +1 및 수락 됨
— Rob

5
+1. 제한적이지만 니스는 아마도 많은 용도를 다룹니다. HTML 섹션을 프로그래밍 방식으로 포함하거나 렌더링이 필요한 항목 집합을 반복하려는 경우에는 유용하지 않습니다.
— AnthonyWJones

최근에 알게되었습니다. 멋지다. 문제에 대한 수업을 작성하기 전에 MSDN 문서를 보는 것이 얼마나 중요한지 알려줍니다. 저는 MailDefinition과 거의 같은 작업을 수행하는 제 클래스를 작성했습니다. 나에게 너무 나쁘다. 시간 낭비.
— MartinHN

4
보낸 사람,받는 사람, 참조 및 숨은 참조 필드를 지정하는 제한된 옵션으로 인해 MailDefinition 클래스를 사용하는 것이 불편했습니다. 또한 웹 UI 컨트롤을위한 네임 스페이스에 의존합니다. ... 내 대답은 아래를 참조
— 세스

이 클래스가 존재한다는 것을 몰랐기 때문에 +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가 가장 좋은 방법 일 것입니다. 그러나 사용하기가 약간 어색 할 수 있으며 일부 개발자는 마크 업 자체를 쉽게 읽을 수 있지만 들여 쓰기와 관련하여 비뚤어진 HtmlTextWriter의 선택은 약간 이상합니다.

이 예제에서는 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
이 슈퍼 구식 보인다
— 다니엘

1
이것이 저에게 가장 좋은 대답이었습니다. 간단하고 요점 (확실히 꽤 투박하지만). MailDefinition은 멋졌지만 내가 찾던 것은 아니 었습니다.
— thehelix

안녕하세요, h1태그에 인라인 스타일을 어떻게 추가 할까요?
— Izzy

이메일 팝업이 열렸을 때 본문, iam 내 컨텍스트에서 div 태그를 사용하여 <25>로 렌더링되었습니다. 올바른 렌더링을 만드는 방법을 마지막 단계로 도와주세요
— clarifier

17

업데이트 된 답변 :

SmtpClient이 답변에 사용 된 클래스에 대한 설명서는 이제 'Obsolete ( "SmtpClient 및 해당 유형의 네트워크가 잘못 설계되었습니다. https://github.com/jstedfast/MailKit 및 https : // github 를 사용하는 것이 좋습니다 . .com / jstedfast / MimeKit 대신 ") '.

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

원래 답변 :

MailDefinition 클래스를 사용하는 것은 잘못된 접근 방식입니다. 예, 편리하지만 원시적이며 웹 UI 컨트롤에 따라 달라집니다. 일반적으로 서버 측 작업에는 의미가 없습니다.

아래에 제시된 접근 방식은 MSDN 문서와 CodeProject.com에 대한 Qureshi의 게시물을 기반으로 합니다.

참고 :이 예제는 포함 된 리소스에서 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 첨부 파일 부분을 제거하는 것이 좋습니다. 또한 msg.Body는이 샘플 코드에 설정되어 있지 않습니다. body 변수를 할당해야한다고 가정합니다.
— Gudlaugur Egilsson 2014 년

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>

여기로

프레드에게,

이메일 본문의 첫 부분이 여기에 표시됩니다.


루프 및 ifs가 필요할 때 가장 많은 옵션을 제공합니다
— CMS

3

마크 업이 너무 복잡하지 않은 한 이렇게 손으로 만든 html을 내보내는 것이 아마도 가장 좋은 방법 일 것입니다. stringbuilder는 약 세 번의 연결 후에 만 ​​효율성 측면에서 보상하기 시작하므로 정말 간단한 물건의 경우 string + string이 할 것입니다.

그 외에는 html 컨트롤 (System.Web.UI.HtmlControls)을 사용하고 렌더링 할 수 있습니다. 그런 식으로 가끔 상속하고 복잡한 조건부 레이아웃에 대한 자신 만의 클래스를 만들 수 있습니다.


1

현재 사용 가능한 일부 템플릿 프레임 워크를 살펴볼 수 있습니다. 그들 중 일부는 MVC의 결과로 파생되었지만 필수는 아닙니다. Spark 는 좋은 것입니다.


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.