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/MailKithttps : // 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.