我有一个 asp.net Web 应用程序,它在注册过程中向用户发送几封电子邮件。现在我将它们与代码内联,但我想将它们放在一个中心位置,在那里我可以在不进入 VS 的情况下编辑它们。
存储这些 HTML 模板的最佳位置/格式是什么?
我有一个 asp.net Web 应用程序,它在注册过程中向用户发送几封电子邮件。现在我将它们与代码内联,但我想将它们放在一个中心位置,在那里我可以在不进入 VS 的情况下编辑它们。
存储这些 HTML 模板的最佳位置/格式是什么?
我将我的 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][2]
直接创建一个实例,并使用[SmtpClient][3]
.
此外,为了在所有电子邮件客户端中获得最佳呈现效果,我使用自己的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;
}
我建议将电子邮件模板存储在 XML 文件中,这样将来可以通过向邮件模板添加属性来实现可扩展性,并且还可以轻松编辑。
希望你被分类,这是我发现并认为更容易的。
然后你可以像这样访问它。
var html = Resources.YourFileName;
记得添加使用
using YourProject.Properties;
这取决于模板更改的频率以及更改者。例如:
由应用程序的用户更改,更改是紧急且可能频繁的:
由开发人员(即您)更改,更改不频繁且不紧急:
您可以将电子邮件模板存储在.html file
. 然后以支持您要包含的参数的方式对其进行格式化。例如
<head>
<title></title>
</head>
<body>
Hello <!--Name--> ,
This is a test template
User Name: <!--UserName-->
.............................
.............................
</body>
</html>
每当您向用户发送电子邮件时,您都希望将模板设置为用户特定的,以便您可以在运行时替换参数。
感谢大家提供有关他们如何处理的见解。我从这里收集了很多知识。我喜欢 @MartinHN 使用带有具体数据模型的 Razor 解析器。
但是,它对我来说效果不佳。
要求:
我必须存储电子邮件模板,以便我可以
随时向利益相关者显示相同的模板。因此,它应该可以通过 Intranet 浏览 - 最好通过 sae 网站作为托管 API。
前端设计师应该能够轻松地修改模板。因此,我想以纯 HTML 格式存储它,这样设计人员就不必处理太多技术细节。
电子邮件模板应该很容易供管理员修改(未来的要求)。在不久的将来,SMS、Screen 会有不同的通知。因此模板是不同的。
基于这些要求,我做了以下工作:
由于我使用的是 MVC,因此我创建了一个名为“STATIC”的文件夹,可以直接浏览它(并且 MVC 引擎/http 处理程序将该文件夹排除在执行其 MVC 活动之外)。
使用这种方法,我可以轻松实现第一个要求,并且可以将链接发送给利益相关者 http://api.aksdfjl.com/static/welcomeemailtemplate.html
每个电子邮件模板都有自己的 html,因此设计人员很容易访问相同的模板并将其作为我的存储库文件夹的快捷方式在他们的存储库中引用。Html 具有内联 CSS,它是完全独立的 html - 每封电子邮件。
最后一个主要要求是关于维护这些设计并且用户可以对其进行修改。那么肯定我根本不想处理文件系统。我现在所做的是将这些通知存储到数据库中,并且我将它们初始化一次。之后,管理面板有一个所见即所得的 html 编辑器,可以让他们快速预览以及控制它应该发送到什么。
现在我想确保轻松处理未来的需求,因为我的公司针对不同的模式引入了不同的通知,例如电子邮件、屏幕、短信通知。我决定借助存储这些答案的模板初始化程序 XML 来扩展软件设计。
所有模板的母亲称为 - MessageTemplates.xml 存储不同的信息,我需要初始化不同类型的模板,即电子邮件、短信、屏幕等。
这是代码现在的样子。
[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 调用,并且所有其他选项都可用并且可以使用。
现在通过这种方法,我让组织中的每个人都感到高兴,并且它具有进一步扩展它的强大力量,因此我也很容易引入新模板。