使用 Razor 產生客製化 Email 內容
5 |
多年下來,寫程式發 Email 通知的需求做過 N 回,其中寄給客戶的通知為求美觀常需採用 HTML 格式,而客戶姓名、通知內容等要隨客戶動態改變,嚴格來說也是一種套表。過去我慣用一套自己發明的「特別註記+Replace」做法,例如:
var tmpl = "<span>[$Name$]</span> 您好,您的等侯順位為<span>[$SeqNo$]</span>";
var dict = new Dictionary<string, string>()
{
["Name"] = "Jeffrey",
["SeqNo"] = "007"
};
var res = System.Text.RegularExpressions.Regex.Replace(
tmpl, @"\[\$(?<n>.+?)\$\]", m =>
{
var n = m.Groups["n"].Value;
return dict.ContainsKey(n) ? dict[n] : "<" + n + ">";
});
Console.WriteLine(res);
Console.Read();
土砲做法雖然簡陋,不能 IF ELSE 也沒法跑迴圈,倒也淺顯易懂,就這麼一用十幾年 XD
最近專案又有類似需求打算重操舊業,轉念一想,一帖老方子從 VB6 寫到 C# 6 未免太不長進,該想想有沒有更好的方法。接著馬上有個念頭浮現腦海 - 寫了那麼多 ASP.NET MVC 見識過 CSHTML 的威力,既然信件內文也是 HTML,為什麼不用 Razor 來套版呢?
爬文很快找到順手的兵刃 – RazorEngine,一個允許在任何 .NET 專案(不必是 ASP.NET MVC)使用 Razor 的程式庫:
來個實例,假設我有個中獎通知函要動態改變中獎者姓名、頭銜、獎項內容與日期,若用 Razor 來寫會像這樣(MailTemplate.cshtml):
@model RazorMailTmpl.Models.MailData
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Razor Mail Template Demo</title>
<style>
li { color: #0000ff; }
.due { color: orangered; }
</style>
</head>
<body>
<p>親愛的 @Model.WinnerName @Model.Title,</p>
<p>感謝您參加部落格讀者2017年終摸彩,在此恭喜您獲得以下獎項:</p>
<ul>
@foreach (var prize in Model.Prizes)
{
<li>@prize</li>
}
</ul>
<p>
請在
<span class="due">@Model.DueDate.ToString("yyyy/MM/dd")</span>
前連絡謎之聲領取獎項。
</p>
<p>再次恭喜您幸運中獎!</p>
<p>Regards,<br/>黑暗執行緒部落格抽獎小組</p>
</body>
</html>
接著宣告一個 MailData 資料型別,這樣在編輯 cshtml 時才能享受強型別與 Inetllisense 提示:
using System;
namespace RazorMailTmpl.Models
{
public class MailData
{
public string WinnerName { get; set; }
public string Title { get; set; }
public string[] Prizes { get; set; }
public DateTime DueDate { get; set; }
}
}
要用 RazorEngine 套表很簡單,先建好 MailData 物件,使用 Engin.Razor.AddTemplate() 載入範本,引擎內建 Cache 機制,接著呼叫 Run 或 Compile 以 Cache Key 取出範本進行編譯運算,很快就能得到套表後的 HTML 字串結果:
using RazorEngine;
using RazorEngine.Templating;
using RazorMailTmpl.Models;
using System;
namespace RazorMailTmpl
{
class Program
{
static void Main(string[] args)
{
var mailData = new MailData()
{
WinnerName = "Jeffrey",
DueDate = new DateTime(2018, 2, 14),
Title = "老司機",
Prizes = new string[]
{
"32G USB 行動碟一支",
"Visual Studio 2017 紀念貼紙一組",
"法拉帝(Ferretti) 660 豪華遊艇(20米)一艘"
}
};
//將Template存入Cache以利重複使用
Engine.Razor.AddTemplate(
"MailBody", // Cache Key
System.IO.File.ReadAllText("MailTemplate.cshtml"));
//傳入Cache Key、Model物件型別、Model物件取得套表結果
var result =
Engine.Razor.RunCompile("MailBody", typeof(MailData), mailData);
//除了RunCompile,也可Compile一次,Run多次以提高效能
Engine.Razor.Compile("MailBody", typeof(MailData));
Engine.Razor.Run("MailBody", typeof(MailData), mailData);
System.IO.File.WriteAllText("Result.html", result);
}
}
}
薑!薑!薑!薑~ 完成。
官方文件的說明挺詳細,相信有 MVC cshtml 經驗的同學很快就能上手,祝大家套表愉快。
Comments
# by 阿翰
驚呆了 一直在想T4模板可以使用razor語法的方法 或許這個方式可以,來嘗試看看
# by Peter
這種方式是不是不支援 @Html.DisplayName 這種 HtmlHelper ?
# by Jeffrey
to Peter, Html 屬性源自 WebViewPage,其型別為 HtmlHelper 又跟 ViewContext、IViewDataContainer 緊密綑綁,硬要在 RazorEngine 使用並非不可能,但得模擬很多 MVC 機制的細節,複雜遠高於其所帶來的便利性,不划算。
# by Alex Lee
這東西 除了 EMail 套版之外 還可以 拿來套用 call 第三方 SOAP 服務時所需的 XML
# by ALEX
此 package 已被標註為 "vulnerabilities detected" https://github.com/advisories/GHSA-ph3v-2hq5-5qfq 針對此漏洞 作者之一的 Matthias Dittrich 有做出說明 (refer : https://github.com/Antaris/RazorEngine/issues/585 ) 此漏洞僅針對以下狀況會受影響 : 1. 使用 IsolatedRazorEngineService 以及 CAS (code access security api) 來控制樣板權限 2. 使用者可以由外部來控制樣板內容