.NET Core使用FluentEmail傳送郵件

yi念之間發表於2020-10-13

前言

    在實際的專案開發中,我們會遇到許多需要通過程式傳送郵件的場景,比如異常報警、訊息、進度通知等等。一般情況下我們使用原生的SmtpClient類庫居多,它能滿足我們絕大多數場景。但是使用起來不夠簡潔,許多場景需要我們自行封裝方法去實現,而且程式碼量非常可觀。慶幸的是,我們有一款非常棒的元件,能滿足我們絕大多數應用場景,而且使用簡單功能強大,就是我們今天要說的FluentEmail,這也是我們實際在專案中正在使用的郵件傳送元件。如果你們在.Net Core中有傳送郵件的需求,也推薦去嘗試一下。

FluentEmail

    FluentEmail是一款在GitHub上開源免費的支援.Net和.Net Core郵件傳送元件,目前已有1K多的Star,而且近兩年隨著.Net Core的日益成熟,它的Star增長趨勢還是非常迅猛的。它在GitHub地址是https://github.com/lukencode/FluentEmail,它的功能非常強大而且非常實用,支援Razor的郵件模板和支援使用SendGrid,MailGun,SMTP傳送郵件,而且使用也非常簡單。

Nuget元件

FluentEmail功能強大,而且對不同場景的支援都有獨立的Nuget包,這種低耦合的拆分不僅使得依賴非常清晰,而且避免引入不需要的程式碼,具體功能包含在以下的元件包中

普通郵件方式

接下來我們就演示一下如何使用FluentEmail傳送郵件,由於我們實際業務中大多數都使用的SMTP的方式傳送郵件,所以我們就以此為做演示,首先我們在專案中引入FluentEmail.Smtp包,目前最新版本為2.8.0

<PackageReference Include="FluentEmail.Smtp" Version="2.8.0" />

接下來我們就可以愉快的寫程式碼了,它的編碼使用方式非常簡單而且非常簡潔,主要通過鏈式程式設計的方式

//如果使用smtp服務傳送郵件必須要設定smtp服務資訊
SmtpClient smtp = new SmtpClient
{
    //smtp伺服器地址(我這裡以126郵箱為例,可以依據具體你使用的郵箱設定)
    Host = "smtp.126.com",
    UseDefaultCredentials = true,
    DeliveryMethod = SmtpDeliveryMethod.Network,
    //這裡輸入你在傳送smtp伺服器的使用者名稱和密碼
    Credentials = new NetworkCredential("郵箱使用者名稱", "郵箱密碼")
};
//設定預設傳送資訊
Email.DefaultSender = new SmtpSender(smtp);
var email = Email
    //傳送人
    .From("zhangsan@126.com")
    //收件人
    .To("lisi@qq.com")
    //抄送人
    .CC("admin@126.com")
    //郵件標題
    .Subject("郵件標題")
    //郵件內容
    .Body("郵件內容");
//依據傳送結果判斷是否傳送成功
var result = email.Send();
//或使用非同步的方式傳送
//await email.SendAsync();
if (result.Successful)
{
    //傳送成功邏輯
}
else
{
    //傳送失敗可以通過result.ErrorMessages檢視失敗原因
}

如果你傳送的內容中包含html格式的內容可以使用如下方式

var email = Email
    //傳送人
    .From("zhangsan@126.com")
    //收件人
    .To("lisi@qq.com")
    //抄送人
    .CC("admin@126.com")
    //郵件標題
    .Subject("郵件標題")
    //只需要額外設定第二個引數為true即可
    .Body("<h1 align=\"center\">.NET大法好</h1><p>是的,這一點毛病都沒有</p>",true);
//傳送
var result = email.Send();

這個我們通過點選檢視Body的方法宣告即可得知第二個引數是用來表示內容是否為html格式,預設為false

IFluentEmail Body (string body, bool isHtml = false);

如果郵件的收件人為多個郵箱地址的話,可以採用To方法的另一個過載方法可以接受List<FluentEmail.Core.Models.Address>

var email = Email
    //傳送人
    .From("zhangsan@126.com")
    //郵件標題
    .Subject("郵件標題")
    //郵件內容
    .Body("<h1 align=\"center\">.NET大法好</h1><p>是的,一點毛病都沒有</p>",true);

//構建多個接收人郵箱
string toUserStr = "oldwang@126.com;xiaoming@163.com;xiaoli@qq.com";
List<FluentEmail.Core.Models.Address> toUsers = toUserStr.Split(";")
    .Select(i => new FluentEmail.Core.Models.Address { EmailAddress = i }).ToList();
//支援傳入Address集合
email.To(toUsers)
//抄送人集合
.CC(toUsers);
//傳送
var result = email.Send();

如果我們需要在傳送的郵件中新增一個附件的話,可以使用Attache方法新增附件

var email = Email
        //傳送人
        .From("zhangsan@qq.com")
        //收件人
        .To("lisi@126.com")
        //抄送人
        .CC("admin@126.com")
        //郵件標題
        .Subject("關於.Net Core怎麼樣")
        //郵件內容
        .Body("<h1 align=\"center\">.NET Core</h1><p>.Net Core很優秀嗎?是的,一點毛病都沒有!!!</p>",true);

//構建附件
var stream = new MemoryStream();
var sw = new StreamWriter(stream);
sw.WriteLine("您好,這是文字里的內容");
sw.Flush();
stream.Seek(0, SeekOrigin.Begin);
var attachment = new FluentEmail.Core.Models.Attachment
{
    Data = stream,
    ContentType = "text/plain",
    Filename = "Hello.txt"
};
//新增附件
email.Attach(attachment);
var result = email.Send();

如果需要新增多個附件的話Attach方法支援傳入Attachment集合

//構建附件
var stream = new MemoryStream();
var sw = new StreamWriter(stream);
sw.WriteLine("您好,這是文字里的內容");
sw.Flush();
stream.Seek(0, SeekOrigin.Begin);
//附件1
var attachment = new FluentEmail.Core.Models.Attachment
{
    Data = stream,
    ContentType = "text/plain",
    Filename = "Hello.txt"
};

//附件2
var attachment2 = new FluentEmail.Core.Models.Attachment
{
    Data = File.OpenRead(@"D:\test.txt"),
    ContentType = "text/plain",
    Filename = "test.txt"
};

//新增附件
email.Attach(new List<FluentEmail.Core.Models.Attachment> { attachment, attachment2 });
var result = email.Send();

使用Razor模板

    上面的內容我們介紹了使用FluentEmail使用常規的方式傳送郵件,但是有時候我們需要傳送一些內容是動態的或者傳送一些樣式比較複雜html網頁內容。通常我們使用原生的SmptClient的時候都是通過拼接html程式碼方式,但是這種方式相對來說比較費時費力,對於.Net程式設計師來說Razor引擎是我們構建動態html頁面最熟悉的方式,而FluentEmail正是為我們提供了Razor模板的支援。首先,我們在之前的基礎上引入FluentEmail.Razor模板支援元件

<PackageReference Include="FluentEmail.Razor" Version="2.8.0" />

由於ASP.NET Core2.2開始預設是使用的檢視編譯功能,檢視會編譯成 專案名稱.Views.dll,但是FluentEmail.Razor又需要讀取檢視檔案的內容,所以要在csproj檔案中新增以下內容

<MvcRazorExcludeRefAssembliesFromPublish>true</MvcRazorExcludeRefAssembliesFromPublish>

然後我們就可以使用Razor模板生成郵件內容,具體的使用方式

//宣告使用razor的方式
Email.DefaultRenderer = new RazorRenderer();
//razor內容
var template = "你好@Model.Name先生, 請核實您的電話號碼是否為@Model.Phone";
var email = Email
    .From("lisi@126.com")
    .To("zhangsan@qq.com")
    .Subject("手機號核實")
    //傳遞自定義POCO類
    //.UsingTemplate<UserInfo>(template, new UserInfo { Name = "張三", Phone嗎 = "100110119120" })
    //或傳遞匿名物件
    .UsingTemplate(template, new { Name = "張三", Phone嗎 = "100110119120" });
var result = await email.SendAsync();

當然它支援的方式不僅僅只是Razor字串,還可以傳遞Razor檢視檔案

var email = Email
    .From("lisi@126.com")
    .To("zhangsan@qq.com")
    .Subject("手機號核實")
    //傳遞自定義POCO類
    //.UsingTemplateFromFile<UserInfo>($"{Directory.GetCurrentDirectory()}/template.cshtml", 
    //     new UserInfo { Name = "張三", Phone嗎 = "100110119120" });
    //第一個引數為檢視檔案位置,第二個引數為模型物件
    .UsingTemplateFromFile($"{Directory.GetCurrentDirectory()}/template.cshtml", 
       new { Name = "張三", Phone嗎 = "100110119120" });
var result = await email.SendAsync();

FluentEmail.Razor之所以能夠支援強大的Razor模板引擎,主要是得益於它內部整合了RazorLight,這是一款非常強大的Razor引擎,可以將Razor模板字串或者Razor檢視檔案解析成具體的字串結果,具體詳情可參閱RazorLight官方GitHub地址https://github.com/toddams/RazorLight,目前正式版並不支援.Net Core,可以選擇下載beta版本

Install-Package RazorLight -Version 2.0.0-beta10

它的使用方式也非常簡單

//razor字串的方式
var engine = new RazorLightEngineBuilder()
	.UseEmbeddedResourcesProject(typeof(Program))
	.UseMemoryCachingProvider()
	.Build();
string template = "Hello, @Model.Name. Welcome to RazorLight repository";
ViewModel model = new ViewModel {Name = "John Doe"};
//result就是解析後的字串
string result = await engine.CompileRenderStringAsync("templateKey", template, model);

或使用razor檢視檔案的方式

var engine = new RazorLightEngineBuilder()
	.UseFileSystemProject("${Directory.GetCurrentDirectory()}")
	.UseMemoryCachingProvider()
	.Build();
var model = new {Name = "John Doe"};
string result = await engine.CompileRenderAsync("template.cshtml", model);

當然它支援的方式不僅僅只有這兩種,無論是使用便捷程度還是功能上都非常的強大,有興趣的同學可以自行查閱RazorLight的GitHub地址,講解的還是非常詳細的。在這裡就不在過多的討論關於RazorLight的使用方式了。
    關於傳送的郵件內容,這裡有一個非常重要的點需要友情提示一下公共郵箱運營商比如網易或騰訊,有的可能需要手動開啟SMTP服務,具體如何設定可以參考https://blog.csdn.net/c13_tianming/article/details/47660635一文。還有一點也比較重要如果你使用公共郵箱運營商的郵箱那麼他們會對郵件的標題和內容限制比較大,可能出現的問題比較多,而且開啟Smtp服務需要傳送簡訊認證才能開啟。好在大部分公司都有自己的郵件系統,在實際傳送郵件的過程中可能不會存在這麼多的問題。

結合依賴注入使用

在使用.Net Core的實際開發中,依賴注入已經成為了必不可少的開發模式。如果你正在使用.Net Core開發專案,但是你還沒有接觸依賴注入,那麼需要你先自行反省一下。FluentEmail作為一款與時俱進的元件,也可以結合依賴注入使用,使用這種方式我們可以在註冊的時候統一的配置一些預設的設定。這波操作就不需要額外引入一些別的包了,如果你需要使用Smtp就引入FluentEmail.Smtp包,如果你需要使用Razor模板就引入FluentEmail.Razor包,關於注入的這一部分的功能其實是包含在FluentEmail.Core包裡面的

public void ConfigureServices(IServiceCollection services)
{
    SmtpClient smtp = new SmtpClient
    {
        //smtp伺服器地址(我這裡以126郵箱為例,可以依據具體你使用的郵箱設定)
        Host = "smtp.qq.com",
        UseDefaultCredentials = true,
        DeliveryMethod = SmtpDeliveryMethod.Network,
        //這裡輸入你在傳送smtp伺服器的使用者名稱和密碼
        Credentials = new NetworkCredential("zhangsan@qq.com", "zhangsan")
    };
    //注入的時候可以新增一些預設的設定
    services
        //設定預設傳送使用者
        .AddFluentEmail("zhangsan@qq.com")
        //新增razor模板支援
        //.AddRazorRenderer($"{Directory.GetCurrentDirectory()}/Views")
        .AddRazorRenderer()
        //配置預設的smtp服務資訊
        .AddSmtpSender(smtp);
}

在需要傳送郵件的類中直接注入IFluentEmail,不必驚慌我們們上面使用的Email這個類其實就是實現了IFluentEmail這個介面,所以使用方式上是完全一致的

public async Task<IActionResult> SendEmail([FromServices]IFluentEmail email)
{
     var result = await email//傳送人
        //傳送人
        .From("zhangsan@126.com")
        //收件人
        .To("lisi@qq.com")
        //抄送人
        .CC("admin@126.com")
        //郵件標題
        .Subject("郵件標題")
        //郵件內容
       .Body("郵件內容").SendAsync();
    return View();
}

如果你需要傳送Razor檢視模板相關的內容,也還是那個熟悉的配方那個熟悉的味道,沒有任何的不同,只是省略了一些我們在註冊的時候新增的一些預設配置

public async Task<IActionResult> SendEmail([FromServices]IFluentEmail email)
{
     var template = "你好@Model.Name先生, 請核實您的電話號碼是否為@Model.Phone";
     var result = await email//傳送人
        .From("lisi@126.com")
        .To("zhangsan@qq.com")
        .Subject("手機號核實")
        //傳遞自定義POCO類
        //.UsingTemplate<UserInfo>(template, new UserInfo { Name = "張三", Phone嗎 = "100110119120" })
        //或傳遞匿名物件
        .UsingTemplate(template, new { Name = "張三", Phone嗎 = "100110119120" })
        .SendAsync();
    return View();
}

總結

    關於FluentEmail的基本使用方式我們就介紹到這裡,我個人感覺它自身的功能還是非常強大的,而且使用起來非常的簡單。說實話在之前我沒接觸到FluentEmail之前,我經常在園子裡看到其他語言整合傳送郵件的元件,確實非常強大,比如在springboot中整合spring-boot-starter-mail真的是非常的便捷。後來無意中接觸到了FluentEmail心裡還是蠻欣慰的,一是它強大的功能和易用性,其次是可以去結合.Net Core進一步優化了它的使用方式,至少在.Net和.Net Core中我們也擁有一款非常便捷的郵件傳送元件。FluentEmail的作者也呼籲更多的開發者能夠了解並參與到FluentEmail開發和實踐中去,最後再次貼上它的GitHub地址https://github.com/lukencode/FluentEmail,有興趣的可以去了解學習一下順便別忘了給個Star。

?歡迎掃碼關注我的公眾號? .NET Core使用FluentEmail傳送郵件

相關文章