存储电子邮件模板的位置应该在哪里?

10
我有一个asp.net Web 应用程序,在注册过程中向用户发送多封电子邮件。现在我将它们与代码一起内嵌,但我想将它们保存在一个集中的位置,以便我可以在不进入VS的情况下进行编辑。
最好的地方/格式来存储这些HTML模板是什么?
6个回答

16
我将所有与我的Web应用程序有关的电子邮件模板存储为ASP.NET MVC Razor Views的形式,但是作为一个轻量级程序集中的嵌入资源,我可以轻松地从任何项目中引用它们。
模板看起来像这样(请注意本地化):
@model Milkshake.Commerce.Model.Users.UserDto
@using Milkshake.Core.Internationalization;
@using Milkshake.Commerce.Model.Meta;

@if (Language.CurrentForInterface.TwoLetterISOLanguageName.Equals("da"))
{

<h1>Hej @Model.FirstName</h1>

<p>
    Din nye brugerkonto til Milkshake Commerce er blevet oprettet.
</p>

<p>
    Gå til dine <a href="http://@ShopSettings.Instance.Domain.TrimEnd('/')/Account">konto indstillinger</a>, brug din e-mail adresse som adgangskode og du vil blive videreført til dine konto indstillinger, hvor du kan ændre din adgangskode.
</p>

<p>Ha' en god dag!</p>
<h2>The Milkshake Commerce Team!</h2>

}
else
{

<h1>Hi @Model.FirstName</h1>

<p>
    Your new user account for Milkshake Commerce has been created for you.
</p>

<p>
    Go to your <a href="http://@ShopSettings.Instance.Domain.TrimEnd('/')/Account">user account page</a>, use your e-mail address as password and you'll be taken directly to your account page where you can change your password.
</p>

<p>Have a nice day!</p>
<h2>The Milkshake Commerce Team!</h2>

}

然后我有一个名为_AppEmailTemplate.cshtml的"主"模板:

@using Milkshake.Commerce.Model.Resources

<!DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd>

<html xmlns="http://www.w3.org/1999/xhtml">

    <head>
        <title></title>

        <style type="text/css">
            body
            {
                font-family: Arial, Helvetica;
            }
            .layout-wrapper
            {
                width: 600px;
            }
            .header
            {
                background-color: #242225;
            }
            .header img
            {
                display: block;
            }
            .content
            {
                background-color: #ffffff; padding: 10px 20px; border: 10px solid #eaeaea; border-top: none;
            }
            .footer
            {
                padding: 20px; padding-top: 5px; font-size: 10px; color: #cccccc;
            }
            p
            {
                font-size: 14px;
            }
            p.company-details
            {
                font-size: 12px;
            }
            h1
            {
                font-size: 20px;
            }
            h2
            {
                font-size: 16px;
            }
        </style>
        <style type="text/css" id="mobile">
            @@media only screen and (max-device-width: 480px) {
                body
                {
                }
                .layout-wrapper
                {
                    width: 480px !important;
                }
                .header
                {
                    background-color: transparent !important;
                }
                .header img
                {
                    width: 480px !important;
                }
                .content
                {
                    border: none !important;
                }
                .footer
                {
                    padding-top: 15px !important;
                }
                p
                {
                    font-size: 22px !important;
                }
                h1
                {
                    font-size: 28px !important;
                }
                h2
                {
                    font-size: 24px !important;
                }
            }
        </style>
    </head>

    <body leftmargin="0" marginwidth="0" topmargin="0" marginheight="0" offset="0" bgcolor="#f1f1f1">

        <table width="100%" cellpadding="0" cellspacing="0" bgcolor="#f1f1f1">
            <tr>
                <td valign="top" align="center">

                    <table cellpadding="0" cellspacing="0" width="100%" height="80">
                        <tr>
                            <td class="header" align="center">
                                <table cellpadding="0" cellspacing="0" width="600" height="80" class="layout-wrapper" style="width: 600px;">
                                    <tr>
                                        <td>
                                            <img src="http://example.com/email-header.png" alt="Milkshake Commerce" />
                                        </td>
                                    </tr>
                                </table>
                            </td>
                        </tr>
                    </table>

                    <table cellpadding="0" cellspacing="0" width="600" class="layout-wrapper">
                        <tr>
                            <td class="content" align="left">
                                #¤#¤CONTENTSECTION#¤#¤
                            </td>
                        </tr>
                        <tr>
                            <td class="footer" align="left">
                                <p>@Text.appEmailDisclaimer</p>
                                <p>@Text.appEmailFooterAd.UrlDecode()</p>
                                <p class="company-details"><i>Company name etc.</i></p>
                            </td>
                        </tr>
                    </table>

                </td>
            </tr>
        </table>

    </body>

</html>

为了实际发送电子邮件,我使用RazorEngine进行渲染:

public void SendSystemEmail<T>(string templateName, string subject, string fromName, string recipientEmail, T model)
{
    dynamic template = this.GetEmailTemplate(templateName);
    string layoutBody = RazorEngine.Razor.Parse(template.Layout as string, model);
    string emailBody = RazorEngine.Razor.Parse(template.Template as string, model);

    emailBody = layoutBody.Replace(CONTENTSECTIONREPLACETOKEN, emailBody);

    PreMailer.Net.PreMailer pm = new PreMailer.Net.PreMailer();
    emailBody = pm.MoveCssInline(emailBody, true);

    EmailDto email = new EmailDto();
    email.Body = emailBody;
    email.IsBodyHtml = true;
    email.FromEmail = "support@example.com";
    email.ReplyToEmail = email.FromEmail;
    email.FromName = fromName;
    email.RecipientEmail = recipientEmail;
    email.Subject = subject;
    email.Type = EmailTypes.Transactional;

    if (String.IsNullOrWhiteSpace(email.FromName))
    {
        email.FromName = "Milkshake Software";
    }

    this.SendMailMessages(new List<EmailDto>() { email }, false);
}

上述代码使用了我的自定义EmailDto对象。在这里,您可以直接创建一个[MailMessage]实例,并使用[SmtpClient]发送它。
此外,为了在所有电子邮件客户端中获得最佳渲染效果,我使用自己的PreMailer.Net库将所有CSS内联移动。阅读我的博客文章以获取更多信息。(代码在Github上)
GetEmailTemplate执行以下操作:
/// <summary>
/// Gets the email template.
/// </summary>
/// <param name="templateName">Name of the template.</param>
/// <returns>Returns the e-mail template.</returns>
private dynamic GetEmailTemplate(string templateName)
{
    string masterTemplateContents = this.GetTemplateFileContents("_AppEmailTemplate.cshtml");
    string templateContents = this.GetTemplateFileContents(templateName + ".html.cshtml");

    return new { Layout = masterTemplateContents, Template = templateContents };
}

/// <summary>
/// Gets the template file contents.
/// </summary>
/// <param name="templateFileName">The name of the template file.</param>
/// <returns>Returns the contents of the template file.</returns>
private string GetTemplateFileContents(string templateFileName)
{
    return this.GetEmailFileContents("Templates", templateFileName);
}

/// <summary>
/// Gets the email file contents.
/// </summary>
/// <param name="lastNamespaceToken">The last namespace token.</param>
/// <param name="templateFileName">The name of the template file.</param>
/// <returns>
/// Returns the contents of the template file.
/// </returns>
private string GetEmailFileContents(string lastNamespaceToken, string templateFileName)
{
    var assembly = Assembly.GetExecutingAssembly();

    if (assembly != null)
    {
        StringBuilder sb = new StringBuilder();

        using (StreamReader sr = new StreamReader(assembly.GetManifestResourceStream(String.Format("MyApp.BusinessLogic.Communication.{0}.{1}", lastNamespaceToken, templateFileName))))
        {
            while (!sr.EndOfStream)
            {
                var line = sr.ReadLine();

                if (!line.StartsWith("@model"))
                {
                    sb.AppendLine(line);
                }
            }
        }

        return sb.ToString();
    }

    return null;
}

1

这取决于模板的更改频率以及更改者是谁。例如:

由应用程序用户更改,更改是紧急且可能频繁的:

  • 最好存储在数据库中,并在每次发送电子邮件时加载。

由开发人员(即您)更改,更改不频繁且不紧急:

  • 在 Web 服务器上使用文本文件,将它们加载到缓存中并在那里存储,仅在缓存下降或应用程序重新启动时重新加载它们。

1
我建议将电子邮件模板存储在XML文件中,这将允许在将来通过向邮件模板添加属性来实现可扩展性,并且还将允许轻松编辑。

我在我的网站中使用了 XML 文件,它允许我大幅度扩展电子邮件模板所支持的功能。 - Samir Adel

1
希望你已经解决了问题,这是我找到的并认为更容易的方法。
  1. 打开项目属性。
  2. 进入资源。在资源下,选择“添加资源” => “添加现有文件”
然后您可以像这样访问它。
var html = Resources.YourFileName;

记得添加using

using YourProject.Properties;

0
你可以将邮件模板存储在一个 .html 文件 中,然后按照支持你想要包含的参数的方式进行格式化。例如:
<head>
<title></title>
</head>
<body>
    Hello <!--Name--> ,
    This is a test template 
    User Name: <!--UserName-->
    .............................
    .............................
</body>
</html>

每当您向用户发送电子邮件时,您都希望将模板设置为特定于用户,以便在运行时替换参数。

0

感谢大家分享他们的处理方式。我从这里收集了很多知识。我喜欢@MartinHN使用具体数据模型的Razor解析器。

然而,有些东西对我来说并不是很有效。

需求:

  • 我必须存储电子邮件模板,以便随时向利益相关者显示相同的内容。因此,它应该可以通过Intranet浏览-最好是通过托管API的sae网站。

  • 前端设计师应该能够轻松修改模板。因此,我想将其存储为纯HTML格式,以便设计师不必过多地了解技术细节。

  • 电子邮件模板应该易于管理员进行修改(未来需求)。在不久的将来,将会有不同的SMS、屏幕通知。因此,模板是不同的。

基于这些要求,我做了以下工作:

  1. 由于我使用的是MVC,我创建了一个名为“STATIC”的文件夹,可以直接访问(并且MVC引擎/ http处理程序排除此文件夹执行其MVC活动)。

    通过这种方法,我可以轻松实现第一个要求,并且可以向利益相关者发送我的链接, http://api.aksdfjl.com/static/welcomeemailtemplate.html

  2. 每个电子邮件模板都有自己的HTML代码,因此设计师能够轻松访问它们并将其作为指向我的存储库文件夹的快捷方式存储在他们的资源库中。该HTML代码具有内联CSS,并且是完全独立的HTML - 每个电子邮件一个。

  3. 最后一个主要需求是维护这些设计并允许用户对其进行修改。那么我绝对不想完全通过文件系统来处理它们。我所做的是现在将这些通知存储到数据库中,并初始化它们一次。之后,管理员面板有一个所见即所得的HTML编辑器,可以快速预览并控制要发送什么内容。

现在我想要确保未来的需求能够轻松处理,因为我的公司正在引入不同模式的通知,如电子邮件、屏幕、短信通知等。我决定通过使用存储这些答案的模板初始化器XML来扩展软件设计。

所有模板的母版称为 - MessageTemplates.xml,其中存储了不同的信息,我需要用它来初始化不同类型的模板,如电子邮件、短信、屏幕等。

enter image description here

现在代码的样子如下。

 [HttpGet]
        [Route("applications/initializenotificationtemplate")]
        public IHttpActionResult InitializeNotificationTemplate()
        {
            return
                InitializeNotificationTemplate(Path.Combine(HostingEnvironment.ApplicationPhysicalPath,
                    @"Static\InitializeData\MessageTemplates.xml"));
        }




[NonAction]
        public IHttpActionResult InitializeMailTemplate(string filePath)
        {
            try
            {
                _applicationService.InitializeTemplate(filePath);
                return Ok("Application Notification templates are initialized.");
            }
            catch (Exception ex)
            {
                return InternalServerError(ex);
            }
        }

_applicationService.InitializeTemplate的定义如下:

public bool InitializeTemplate(string filePath)
        {
            if (string.IsNullOrEmpty(filePath))
            {
                throw new ArgumentNullException("File Path");
            }

            if (!File.Exists(filePath))
            {
                throw new FileNotFoundException(filePath);
            }


            var data = _notificationTemplateService.Get();
            var exceptionMessages = string.Empty;

            if (data != null)
            {
                var historicalTemplates = data.ToList();
                historicalTemplates.ForEach((d) => _notificationTemplateService.Delete(d, out exceptionMessages));
            }

            XDocument xmlDocument = XDocument.Load(filePath);
            IEnumerable<NotificationTemplate> templates = (from template in xmlDocument.Descendants("Template")
                                                           select new NotificationTemplate()
                                                           {
                                                               Title = template.Element("Subject").Value,
                                                               Description = template.Element("Body").Value,
                                                               Type = (NotificationTypeOptions)Enum.Parse(typeof(NotificationTypeOptions), template.Element("Type").Value, true),
                                                               Category = (NotificationCategoryOptions)Enum.Parse(typeof(NotificationCategoryOptions), template.Attribute("category").Value, true),
                                                           }).ToList();

            foreach (var t in templates)
            {
                var path = Path.Combine(Path.GetDirectoryName(filePath), Regex.Replace(t.Description, @"\t|\n|\r| ", ""));
                if (File.Exists(path))
                {
                    StreamReader reader = new StreamReader(path);
                    t.Description = reader.ReadToEnd();
                }
                else
                {
                    t.Description = string.Empty;
                }
            }

            return _notificationTemplateService.InsertRange(templates, out exceptionMessages);
        }

这是我的模型的样子,与数据库模型相同(代码优先 - EF方法)。

 public class NotificationTemplate  : IdentityBase
    {
        public string Category { get; set; }
        public NotificationTypeOptions Type { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }

        public NotificationTemplate()
        {
            Type = NotificationTypeOptions.Email;
        }
    }

 [Flags]
    public enum NotificationTypeOptions
    {
        Email = 0,
        Screen = 1,
    }

第一次安装我的应用程序时,我调用初始化API调用,将我的通知模板安装到数据库中,并且所有其他选项都可用并准备好使用。

现在,通过这种方法,我让组织中的每个人都感到满意,并且它具有进一步扩展的巨大优势,因此我可以轻松地引入新模板。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接