前言:模板引擎渲染HTML的技术有很多,这里仅说明一下利用.net core下的mvc框架里的razor引擎怎么去做模板渲染功能。 在介绍之前,首先感谢磊哥的技术分享,得以让这篇文章成型。
核心参考文章:
Walkthrough: Creating an HTML Email Template with Razor and Razor Class Libraries
https://github.com/scottsauber/RazorHtmlEmails
a-razor引擎渲染HTML核心代码:
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.ViewEngines; using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Routing; using System; using System.IO; using System.Linq; using System.Threading.Tasks; namespace MeShop.CRM.Domain.Services.Vip.Helper { // Code from: https://github.com/aspnet/Entropy/blob/master/samples/Mvc.RenderViewToString/RazorViewToStringRenderer.cs public class RazorViewToStringRenderer : IRazorViewToStringRenderer { private IRazorViewEngine _viewEngine; private ITempDataProvider _tempDataProvider; private IServiceProvider _serviceProvider; public RazorViewToStringRenderer( IRazorViewEngine viewEngine, ITempDataProvider tempDataProvider, IServiceProvider serviceProvider) { _viewEngine = viewEngine; _tempDataProvider = tempDataProvider; _serviceProvider = serviceProvider; } public async Task<string> RenderViewToStringAsync<TModel>(string viewName, TModel model) { var actionContext = GetActionContext(); var view = FindView(actionContext, viewName); using (var output = new StringWriter()) { var viewContext = new ViewContext( actionContext, view, new ViewDataDictionary<TModel>( metadataProvider: new EmptyModelMetadataProvider(), modelState: new ModelStateDictionary()) { Model = model }, new TempDataDictionary( actionContext.HttpContext, _tempDataProvider), output, new HtmlHelperOptions()); await view.RenderAsync(viewContext); return output.ToString(); } } private IView FindView(ActionContext actionContext, string viewName) { var getViewResult = _viewEngine.GetView(executingFilePath: null, viewPath: viewName, isMainPage: true); if (getViewResult.Success) { return getViewResult.View; } var findViewResult = _viewEngine.FindView(actionContext, viewName, isMainPage: true); if (findViewResult.Success) { return findViewResult.View; } var searchedLocations = getViewResult.SearchedLocations.Concat(findViewResult.SearchedLocations); var errorMessage = string.Join( Environment.NewLine, new[] { $"Unable to find view ‘{viewName}‘. The following locations were searched:" }.Concat(searchedLocations)); ; throw new InvalidOperationException(errorMessage); } private ActionContext GetActionContext() { var httpContext = new DefaultHttpContext(); httpContext.RequestServices = _serviceProvider; return new ActionContext(httpContext, new RouteData(), new ActionDescriptor()); } } public interface IRazorViewToStringRenderer { Task<string> RenderViewToStringAsync<TModel>(string viewName, TModel model); } }
b-邮件模板模块:
需要注意的是:邮件模板模块,必须放在mvc项目的Views目录下,主要是利用Razor寻找页面元素位置的机制(或者可以自定义寻找视图位置)
TemplateShopExpired实体示例:
namespace MeShop.Module.Model.CRM.EmailTemplate { /// <summary> /// 邮件模板类-店铺过期 /// </summary> public class TemplateShopExpired { public string ShopHost { get; set; } public string ShopHostAdmin { get; set; } public string ShopSaveDays { get; set; } public string ShopCloseDate { get; set; } public string CdnHost { get; set; } } }
EmailTemplateShopExpired.cshtml文件示例:
@model MeShop.Module.Model.CRM.EmailTemplate.TemplateShopExpired @{ Layout = null; } <!DOCTYPE HTML> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title></title> </head> <body> <table width="650" border="0" align="center" cellpadding="0" cellspacing="0" style="border: 1px solid #ccc;"> <tbody> <tr> <td valign="top" width="335"> <span class="edit-emai-text" style="font-size:20px;color:#000;font-weight: bold;font-family: arial;display:block;padding:20px 0 20px 20px;"> <span class="edit-email-child">欢迎来到meshop</span> </span> </td> </tr> <tr> <td valign="top" width="335"> <span class="edit-emai-text" style="font-size:15px;color:#000;font-weight:bold;font-family: arial;display:block;padding:20px 20px 15px 20px;"> <span class="edit-email-child">您的店铺<a href="@(Model.ShopHost)/admin/home">@(Model.ShopHostAdmin)</a>已经到期, 我们将为您保留@ShopSaveDays@天的店铺配置数据,@(Model.ShopCloseDate)(UTC)后我们将清除店铺的配置数据,只保留店铺注册信息。</span> </span> </td> </tr> <tr> <td valign="top" width="335"> <span class="edit-emai-text" style="font-size:16px;color:#000;font-weight:bold;font-family: arial;display:block;padding:20px 0 15px 20px;"> <a class="edit-emai-link" href="@(Model.ShopHost)/admin/home" title="" style="border:1px solid #ccc;font-size:14px;border-radius: 3px; padding:5px 3px; color:#000; text-decoration:none"><span class="edit-email-child"> 立即续费 </span></a> </span> </td> </tr> </tbody> </table> <TABLE style="PADDING-BOTTOM: 10px; FONT-FAMILY: Arial; MARGIN-BOTTOM: 10px; MARGIN-LEFT: auto; FONT-SIZE: 12px; MARGIN-RIGHT: auto; PADDING-TOP: 15px" border=0 cellSpacing=0 cellPadding=0 width=650 bgColor=#ffffff align=center> <TBODY> <TR> <TD vAlign=center width=650 align=middle> <SPAN class="edit-emai-text" style="PADDING-BOTTOM:15px; DISPLAY: inline-block;vertical-align: middle;"> <img src="@(Model.CdnHost)/admin/favicon.ico" style="display:inline-block;width:40px;height:auto; vertical-align: middle;"> <span class="edit-emai-text" style=" DISPLAY: inline-block;FONT-SIZE:20px;font-weight:bold;">meshop</span> </SPAN> </TD> </TR> </TBODY> </TABLE> </body> </html>
c-使用示例:
/// <summary> /// 3-发送店铺过期邮件 /// </summary> /// <param name="shopID">店铺ID</param> /// <param name="times">次数</param> public async Task SendShopExpiredEmail(long shopID, int times) { ShopInfo shopInfo = base.ShopInfoRepository.Get(shopID); if (shopInfo != null) { string shopSsoHost = PayCommonHelper.GetSsoHost(shopInfo.HostAdmin); //生成店铺过期时间执行SouceValue(店铺ID+过期时间+次数) string shopExpiredSourceValue = $"{shopID}_{shopInfo.ExpiredTime.ToString_yyyyMMddHHmmss()}_{times}"; TemplateShopExpired templateShopExpired = new TemplateShopExpired { ShopHostAdmin = shopInfo.HostAdmin, ShopHost = shopSsoHost, ShopSaveDays = (shopInfo.BufferTime - shopInfo.ExpiredTime).Days.ToString(), ShopCloseDate = shopInfo.BufferTime.ToString_yyyyMMddHHmmss(), CdnHost = PayCommonHelper.GetCdnHost(this._config) }; string emailTemplateContent = await this._razorViewToStringRenderer.RenderViewToStringAsync(this._emailTmplateShopExpiredRazorPath, templateShopExpired); } }