前言
在實際的項目開發中,我們會遇到許多需要通過程式發送郵件的場景,比如異常報警、消息、進度通知等等。一般情況下我們使用原生的 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.Core - 基础核心包,包含了基础的模型定义和默认的设置,而且以下的引用包都包含了这个核心包。
- FluentEmail.Smtp - 使用 SMTP 服务发送邮件的程序包。
- FluentEmail.Razor - 通过 Razor 模板生成邮件发送内容。
- FluentEmail.Mailgun - 使用 Mailgun 的 Rest 接口发送邮件。
- FluentEmail.SendGrid - 使用 SendGrid 接口发送邮件。
- FluentEmail.Mailtrap - 发送邮件 Mailtrap, 使用的是 FluentEmail.Smtp 包进行发送.
- FluentEmail.MailKit - 使用 MailKit 邮件库发送邮件。
普通郵件方式
接下來我們就演示一下如何使用 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。