c# - 在 ASP.NET MVC 项目中使用 Razor 生成电子邮件 HTML

标签 c# .net razor

我有一个 ASP.NET MVC 项目,它使用 Razor 通过内置引擎呈现 HTML。

我想使用相同的过程创建电子邮件模板。通常,这些模板是作为 Action Context 的一部分创建的(例如,当用户完成购买时,将发送通知)。但是,有些情况下没有可用的上下文。例如,在应用程序重新启动时发送日志。

这是我到目前为止的想法:

public static string RenderRazor(ViewTemplateType TemplateType, string ViewName, PageController Controller = null, object Model = null)
{
    try
    {
        ControllerContext Context;

        if (Controller != null)
        {
            Context = Controller.ControllerContext;
        }
        else
        {
            if (HttpContext.Current == null)
            {
                throw new InvalidOperationException("Cannot render a razor template if the current context is null (See DEV-1669).");
            }
            var RouteData = new RouteData();
            RouteData.Values.Add("controller", "Pseudo");
            Controller = new PseudoController(Model);
            Context = new ControllerContext(new HttpContextWrapper(HttpContext.Current), RouteData, Controller);
            // If this isn't set, an error occurs when calling FindView/FindViewPartial.
            Controller.ControllerContext = Context;
        }

        // I'm not really sure what the point of this is...
        // Further, it was actually causing an exception to occur since the Controller may not actually be populated?
        // Without this, the Notification Debug wasn't working - so apparently it is required in some circumstances for notifications.
        if (Controller != null && Controller.ViewData != null && Model != null) { Controller.ViewData.Model = Model; }

        var ViewResult = ViewName.StartsWith("_")
            ? ViewEngines.Engines.FindPartialView(Context, string.Format("~/Views/Template/{0}/{1}.cshtml", TemplateType, ViewName))
            : ViewEngines.Engines.FindView(Context, string.Format("~/Views/Template/{0}/{1}.cshtml", TemplateType, ViewName), string.Format("~/Views/Template/{0}/_Shared/{1}.cshtml", TemplateType, "_Layout"));

        if (ViewResult.View == null)
        {
            StringBuilder LocationBuilder = new StringBuilder();
            string[] SearchedLocations = ViewResult.SearchedLocations.ToArray();
            for (int i = 0; i < SearchedLocations.Length; i++)
            {
                LocationBuilder.Append(string.Format("({0}) {1} ", i, SearchedLocations[i]));
            }

            throw new InvalidOperationException(string.Format("Could not find the {0} Template named {1} using the {2} Master. Locations Searched: {3}", TemplateType, ViewName, "_Layout", LocationBuilder));//
        }
        using (var Writer = new StringWriter())
        {
            //ViewResult.View.Render(new ViewContext(
            //  Context,
            //  ViewResult.View,
            //  (Controller != null) ? Controller.ViewData : (Model != null) ? new ViewDataDictionary(Model) : new ViewDataDictionary(),
            //  (Controller != null) ? Controller.TempData : new TempDataDictionary(), Writer), Writer);

            ViewResult.View.Render(new ViewContext(
                Context,
                ViewResult.View,
                Controller.ViewData,
                Controller.TempData, Writer), Writer);

            ViewResult.ViewEngine.ReleaseView(Context, ViewResult.View);

            // This must remove Tabs (\t) Returns (\r) and Newlines (\n)
            // Always making the quotes single makes sense for statically generated stuff - the only time when it wouldn't make sense is for more complex stuff or if it includes JS which i don't think
            // a statically generated one should ever?

            string result = Regex.Replace(Writer.GetStringBuilder().ToString().Replace("\"", "\'"), "(\\t|\\r|\\n)", string.Empty);

            // Currently, this process does not work well when initiated outside of a request (e.g. in the startup method or purely within a
            // hangfire task). This serves as a warning if it ever is (since it will return an empty string).

            if (result.Blank()) { throw new InvalidOperationException("There was an error rendering the " + ViewName + " template. This can happen if the template was initialized outside of the context of an actual request."); }
            else
            {
                return result;
            }
        }
    }
    catch (Exception ex)
    {
        // This could indicate an error in the underlying template
        // If there is an error on any of the underlying templates in a given class,
        // this can be called.
        Logging.Error(ex);
        return string.Empty;
    }
}

这工作得很好,除非我在没有对 Controller 的引用时尝试生成模板。

我也开始考虑应用第三方 RazorEngine ( https://antaris.github.io/RazorEngine ) - 但这是不是有点矫枉过正?在同样使用内置 Razor 引擎的项目中实现这一点是否可行?

最佳答案

我用过 ActionMailer在过去。它不再维护,但有一个 fork https://github.com/crossvertise/ActionMailerNext .

您设置一个 Controller 来绑定(bind)模板,但可以像调用另一个服务类一样调用它。

public class EmailController : MailerBase, IEmailService
{
    public EmailResult PasswordRecovery(PasswordRecoveryModel model)
    {
        To.Add(model.Email);
        From = "noreply@example.com";
        Subject = "Password Recovery";
        return Email("PasswordRecovery", model);
    }
}

我用我自己的接口(interface)设置它,所以我可以在任何地方注入(inject)它

public interface IEmailService
{
    EmailResult PasswordRecovery(PasswordRecoveryModel model);
}


public class Foo
{
    private readonly IEmailService emailService;

    public Foo(IEmailService emailService)
    {
        this.emailService = emailService;
    }

    public void DoSomething()
    {
        this.emailService.PasswordRecovery(new PasswordRecoveryModel { ... });
    }
}

模板看起来就像常规 View

密码恢复.html.cshtml

@using ActionMailer.Net.Mvc
@model PasswordRecoveryModel

<p>@Model.UserName</p>
<div> ... </div>

我还没有尝试过 fork ,所以我只能假设它在用法上是相似的。

关于c# - 在 ASP.NET MVC 项目中使用 Razor 生成电子邮件 HTML,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47403086/

相关文章:

c# - 应该使用 Dispose() 或 Finalize() 来删除临时文件吗?

c# - Client-Unet 中未收到广播

c# - 如何设置 UShort 变量的 MSB?

c# - 在 asp.net mvc 应用程序中从 Javascript 访问 C# 变量

c# - 如何在运行时向 TypeDescriptor 添加属性级属性?

.net - 在 .Net 中找出 Unicode 字符名称

.net - 在 RHPL 中找不到框架 “.NETFramework,Version=v4.5.1” 的引用程序集

c# - 有没有办法根据任意 xml 获取 System.Configuration.Configuration 实例?

c# - 如何在 Razor 代码中的 ASP.NET-MVC-5 中为强类型数据模型列表取多个值

asp.net-mvc - TD 表格的良好格式