【问题标题】:RazorEngine layoutsRazorEngine 布局
【发布时间】:2012-07-09 23:33:17
【问题描述】:

我正在使用 Razor 引擎 https://github.com/Antaris/RazorEngine 来解析我的电子邮件模板的正文。是否可以定义布局并包含其他 .cshtml 文件?例如常见的页眉和页脚。

【问题讨论】:

    标签: c# razorengine


    【解决方案1】:

    实现邮件功能的完全自定义解决方案。

    添加RazorEngine

    的nuget包

    创建 _Layout 模板(.cshtml):

    <html>
    <body style="margin: 0; padding: 0;">
        <div style="width:100%; display:block; float:left; height:100%;">
            <table cellpadding="0" cellspacing="0" border="0" align="center" width="100%">
                <tr>
                    <td width="37" style="background-color: #ffffff;"></td>
                    <td width="200" style="background-color: #ffffff">
                        <a href="@Url("")">Send Mail Logo</a>                    
                    </td>
                    <td style="background-color: #ffffff;">
                        &nbsp;
    
                    </td>
                    <td width="126" style="background-color: #ffffff;">
                        <img src="@Url("Images/mail/social-media.png")" alt="" width="126" height="73" border="0" usemap="#Map" />
                    </td>
                </tr>
            </table>
            <table cellpadding="0" cellspacing="0" border="0" align="center" width="100%">
                <tr height="7">
                    <td style="background-color: #ffffff;" colspan="3"></td>
                </tr>
                <tr height="54">
                    <td colspan="3"></td>
                </tr>
                <tr>
                    <td width="37">&nbsp;</td>
                    <td style="font-family: Myriad, 'Helvetica Neue',Arial,Helvetica,sans-serif; font-size: 11pt; color: #6b6c6f; line-height: 24px;">
                        {{BODY}}
                    </td>
                    <td width="37">&nbsp;</td>
                </tr>
    
                <tr height="55">
                    <td style="line-height: 0;" colspan="3">&nbsp;</td>
                </tr>
                <tr height="11">
                    <td background="@Url("/Images/mail/dotted-line.png")" colspan="3" style="line-height: 0;">&nbsp;</td>
                </tr>
            </table>
        </div>
        <map name="Map" id="Map">
            <area shape="rect" coords="28,29,51,51" href="#" alt="Twitter" />
            <area shape="rect" coords="56,28,78,52" href="#" alt="Google+" />
            <area shape="rect" coords="84,28,104,51" href="#" alt="LinkedIn" />
        </map>
    </body>
    </html>
    

    创建 ConfirmEmail 模板 (.cshtml):

    @using yourProjectnamespace.LanguageResources.Mail
    @model ConfirmEmail
    
    @MailTemplateResource.YouHaveLoggedIn
    
    <a href="@Url(string.Format("/User/Confirmemail?EmailId={0}", Model.UserId))">@MailTemplateResource.ClickHere</a> 
    

    创建 CustomTemplateBase 类:

    public class CustomTemplateBase<T> : TemplateBase<T>
        {
            public string Url(string url)
            {
                return MailConfiguration.BaseUrl + url.TrimStart('/');
            }          
        }
    

    创建 EmbeddedTemplateManager 类:

    内部类 EmbeddedTemplateManager : ITemplateManager { 私有只读字符串 ns;

    public EmbeddedTemplateManager(string @namespace)
    {
        ns = @namespace;
    }
    
    public ITemplateSource Resolve(ITemplateKey key)
    {
        var resourceName = $"{ns}.{key.Name}.cshtml";
        string content;
    
        using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
        using (var streamReader = new StreamReader(stream))
        {
            content = streamReader.ReadToEnd();
        }
    
        return new LoadedTemplateSource(content);
    }
    
    public ITemplateKey GetKey(string name, ResolveType resolveType, ITemplateKey context)
    {
        return new NameOnlyTemplateKey(name, resolveType, context);
    }
    
    public void AddDynamic(ITemplateKey key, ITemplateSource source)
    {
        throw new NotImplementedException("");
    }
    

    }

    创建邮件类:

    public class Mail
        {
            private static readonly IRazorEngineService RazorEngine;
    
            static Mail()
            {
                var config = new TemplateServiceConfiguration
                {
                    BaseTemplateType = typeof(CustomTemplateBase<>),
                    TemplateManager = new EmbeddedTemplateManager(typeof(Mail).Namespace + ".Templates"),
                    Namespaces = { "Add CurrentProjectName", "Add CurrentProjectName .Models" },
                    CachingProvider = new DefaultCachingProvider()
                };
                RazorEngine = RazorEngineService.Create(config);
            }
    
            public Mail(string templateName)
            {
                TemplateName = templateName;
                ViewBag = new DynamicViewBag();
            }
    
            public string TemplateName { get; set; }
    
            public object Model { get; set; }
    
            public DynamicViewBag ViewBag { get; set; }
    
            public string GenerateBody()
            {
                var layout = RazorEngine.RunCompile("_Layout", model: null);
                var body = RazorEngine.RunCompile(TemplateName, Model.GetType(), Model);
                return layout.Replace("{{BODY}}", body);
            }
    
            public MailMessage Send(Guid key, string to, string subject, string cc = null)
            {
                var email = new MailMessage()
                {
                    From = MailConfiguration.From,
                    Body = GenerateBody(),
                    IsBodyHtml = true,
                    Subject = subject,
                    BodyEncoding = Encoding.UTF8
                };
    
                email.Headers.Add("X-MC-Metadata", "{ \"key\": \"" + key.ToString("N") + "\" }");         
    
                foreach (var sendTo in to.Split(' ', ',', ';'))
                {
                    email.To.Add(sendTo);
                }
    
                if (cc != null)
                {
                    foreach (var sendCC in cc.Split(' ', ',', ';'))
                    {
                        email.CC.Add(sendCC);
                    }
                }
    
                var smtp = new MailClient().SmtpClient;
                smtp.EnableSsl = true;
                smtp.Send(email);
                return email;
            }
        }
    
        public class Mail<TModel> : Mail where TModel : class
        {
            public Mail(string templateName, TModel mailModel) : base(templateName)
            {
                Model = mailModel;
            }
        }
    

    创建 MailClient 类:

    public class MailClient
        {
            public MailClient()
            {
                SmtpClient = new SmtpClient(MailConfiguration.Host)
                {
                    Port = MailConfiguration.Port,
                    Credentials = new NetworkCredential
                    {
                        UserName = MailConfiguration.UserName,
                        Password = MailConfiguration.Password
                    }
                };
            }
    
            public SmtpClient SmtpClient { get; }
        }
    

    创建 MailConfiguration 类:

    public class MailConfiguration
        {
            private static string GetAppSetting(string key)
            {
                var element = ConfigurationManager.AppSettings["Mail:" + key];
                return element ?? string.Empty;
            }
    
            public static string BaseUrl => GetAppSetting("BaseUrl");     
    
            public static string Host => GetAppSetting("Host");
    
            public static int Port => Int32.Parse(GetAppSetting("Port"));
    
            public static string UserName => GetAppSetting("Username");
    
            public static string Password => GetAppSetting("Password");
    
            public static MailAddress From => new MailAddress(GetAppSetting("From"));
        }
    

    MailSender 类:

    在 MailSerder 类中实现您的方法并在您的存储库或控制器中调用 MailSerder 方法。

    Create public class MailSender : IMailSender
        {
            public MailSender()
            {
    
            }
    
            public void SendConfirmEmail(string emailId, Guid userId)
            {
                var confirmEmail = new ConfirmEmail
                {
                    UserId = userId
                };
                ConfirmEmail(emailId, MailResource.YourRegistration, confirmEmail);
            }
    
            private void ConfirmEmail(string recipient,string subject,ConfirmEmail model)
            {
                var key = Guid.NewGuid();
                var mail = new Mail<ConfirmEmail>("ConfirmEmail", model);
                mail.ViewBag.AddValue("Recipient", recipient);           
                var sentMail = mail.Send(key, recipient, subject);          
            }
        }
    

    【讨论】:

      【解决方案2】:

      使用 RazorEngine 实现布局的最简单方法是替换模板在布局的 @RenderBody() 中返回的内容:

       var finalHtml = layout.Replace(@"@RenderBody()", templateHtml);
      

      例如:

      您的 _Layout.cshtml 与典型的 @RenderBody()

      <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html>
          <head>
          </head>
          <body>
              <div>
                  @RenderBody()
              </div> 
          </body> 
      </html>
      

      您的 RazorEngine 模板 MyTemplate.cshtml

      @using RazorEngine.Templating
      @inherits TemplateBase<myviewmodel>
      
      <h1>Hello People</h1>
      <p>@Model</p>
      

      无论您在哪里调用 RazorEngine 模板:

      var TemplateFolderPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "EmailTemplates");
      var template = File.ReadAllText(Path.Combine(TemplateFolderPath,"MyTemplate.cshtml"));
      var layout = File.ReadAllText(Path.Combine(TemplateFolderPath, "_Layout.cshtml"));
      var templateService = new TemplateService();
      var templateHtml = templateService.Parse(template, myModel, null, null);
      var finalHtml = layout.Replace(@"@RenderBody()", templateHtml);
      

      【讨论】:

        【解决方案3】:

        在这两篇文章的帮助下,我得到了常用模板和布局:

        RazorEngine string layouts and sections?

        http://blogs.msdn.com/b/hongyes/archive/2012/03/12/using-razor-template-engine-in-web-api-self-host-application.aspx

        这是我的解决方案:

        解决方案 1: 布局

        通过设置_Layout使用

        @{
            _Layout = "Layout.cshtml";
            ViewBag.Title = Model.Title;
         }
        

        页脚

        @section Footer 
        {
           @RenderPart("Footer.cshtml")
        }
        

        Layout.cshtml

        <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html>
            <head>
            </head>
            <body>
                <div id="content">
                    @RenderBody()
                </div> 
                @if (IsSectionDefined("Footer"))
                { 
                    <div id="footer">
                        @RenderSection("Footer")
                    </div>
                }
            </body> 
        </html>
        

        TemplateBaseExtensions

        使用 RenderPart 方法扩展 TemplateBase

        public abstract class TemplateBaseExtensions<T> : TemplateBase<T>
        {
            public string RenderPart(string templateName, object model = null)
            {
                string path = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "Templates", templateName);
                return Razor.Parse(File.ReadAllText(path), model);
            }
        }
        

        Razor 配置

        将 BaseTemplateType 设置为您的 TemplateBaseExtensions 类

        TemplateServiceConfiguration templateConfig = new TemplateServiceConfiguration
        {
             BaseTemplateType = typeof(TemplateBaseExtensions<>)
        };
        
        Razor.SetTemplateService(new TemplateService(templateConfig));
        

        编辑解决方案 2:

        如果您使用的是 TemplateResolver。不需要 RenderPart 使用 @Include 代替

        页脚

        @section Footer 
        {
           @Include("Footer.cshtml")
        }
        

        解析器

        public class TemplateResolver : ITemplateResolver
        {
            public string Resolve(string name)
            {
                if (name == null)
                {
                    throw new ArgumentNullException("name");
                }
        
                string path = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "Templates", name);
                return File.ReadAllText(path, System.Text.Encoding.Default);
            }
        }
        

        配置

        TemplateServiceConfiguration templateConfig = new TemplateServiceConfiguration
        {
             Resolver = new TemplateResolver()
        };
        Razor.SetTemplateService(new TemplateService(templateConfig));
        

        松饼人更新 指定模板并渲染字符串

        var templateResolver = Razor.Resolve("Registration.cshtml");
        return templateResolver.Run(new ExecuteContext());
        

        此外,我和此链接中的其他人 https://github.com/Antaris/RazorEngine/issues/61 在使用 _Layout 时遇到问题,而 Layout 有效。

        '_Layout' 是旧语法。在未来的版本中已更新为“布局”。

        【讨论】:

        • 我尝试在上面的 MS 博客中实施解决方案,但我不断收到 stackoverflow 异常。我花了一整天的时间。
        • 我可以给你发一份我拼凑起来的快速申请,让你明白
        • rapidshare.com/files/3962348204/… 它被拼凑在一起,但你肯定会明白的。应用程序基础是 RazorEngineConsoleApplication\bin\Debug 所以我将模板复制到那里。坚持我发布的示例。
        • 而不是@RendarPart 使用@Include("Footer.cshtml", Model) 它将使用模板解析器。不需要 RendarPart 扩展
        • ITemplateResolver 是票。否则什么也做不了。最终编写了一个自定义路径处理程序来消除搜索视图时的任何怪癖。
        【解决方案4】:

        你可以用 Razor 轻松地做很多事情;然而,那个特定的项目似乎抽象出了很多你可以做的 Razor 引擎的东西(这有好有坏)。在您的情况下,听起来您最好实现自己的 Razor 解决方案(实际上并没有那么糟糕),然后您可以让您的模板抛出异常或很容易地拉入其他内容。

        例如;滚动你自己的解决方案允许你为你的剃刀模板创建一个基类,它可以通过调用其他模板公开拉入“部分视图”的能力。此外,如果某些属性为空,您可以进行模型检查并抛出异常。

        【讨论】:

          猜你喜欢
          • 2012-10-30
          • 2012-05-12
          • 2012-05-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-04-05
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多