2017-11-20 8 views
2

Razorを使用して組み込みエンジンを使用してHTMLをレンダリングするASP.NET MVCプロジェクトがあります。ASP.NET MVCプロジェクトでRazorを使用して電子メールHTMLを生成

同じプロセスを使用して電子メールテンプレートを作成したいと思います。通常、これらのテンプレートはアクションコンテキストの一部として作成されます(たとえば、ユーザーが購入を完了したときに通知が送信されます)。ただし、利用可能なコンテキストがない場合があります。たとえば、アプリケーションの再起動時にログを送信します。

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; 
    } 
} 

これは私がコントローラーへの参照を持っていない時にテンプレートを生成しようとしているとき以外はかなりうまく機能:

これは私がこれまでに出ているものです。

サードパーティのRazorEngine(https://antaris.github.io/RazorEngine)の適用についても検討し始めましたが、これは過剰ですか?組み込みの剃刀エンジンを使用するプロジェクトでこれを実装することも可能ですか?

答えて

1

私は過去にActionMailerを使用しました。それはもはや維持されていますが、フォークhttps://github.com/crossvertise/ActionMailerNextがあります。

テンプレートをバインドするコントローラを設定しますが、別のサービスクラスと同じように呼び出すことができます。私は

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 { ... }); 
    } 
} 

は、テンプレートは普通の景色のように見えるどこ

PasswordRecovery.html.cshtml

@using ActionMailer.Net.Mvc 
@model PasswordRecoveryModel 

<p>@Model.UserName</p> 
<div> ... </div> 
を、それを注入することができますので、私は自分のインターフェイスでそれを設定

public class EmailController : MailerBase, IEmailService 
{ 
    public EmailResult PasswordRecovery(PasswordRecoveryModel model) 
    { 
     To.Add(model.Email); 
     From = "[email protected]"; 
     Subject = "Password Recovery"; 
     return Email("PasswordRecovery", model); 
    } 
} 

私はフォークを試していないので、使用方法も同様です。

関連する問題