如何在 ASP.NET Core 1.0 中发送电子邮件






4.10/5 (9投票s)
如何在 ASP.NET Core 1.0 中发送电子邮件
ASP.NET Core 1.0 是 ASP.NET 框架的重启版本,可以支持传统的 .NET Framework 或新的 .NET Core 框架。ASP.NET Core 和 .NET Core 一起被设计成跨平台工作,并且相比当前的 .NET Framework,它们拥有更轻、更快的占用空间。许多 .NET Core API 与完整框架中的 API 相同,并且团队努力在合理且可行的情况下保持事物之间的相似性。然而,作为开发一个更小、更模块化的依赖库框架以及最重要的是支持跨平台开发和托管的结果;一些库已经丢失。查看 Immo Landwerth 的这篇 文章,其中更详细地描述了这些更改,并讨论了将现有应用程序移植到 .NET Core 的注意事项。
我已经使用 ASP.NET Core 几个月了,总的来说,我还是很享受这段经历。个人而言,我一路遇到的问题很少,并且希望将来尽可能继续使用新框架。然而最近,我在一个项目中遇到了一个障碍,我需要从我的 Web 应用程序中发送电子邮件。在完整框架中,我会使用 `system.net.mail` 命名空间中的 `SmtpClient` 类。然而在 .NET Core 中,目前我们无法使用它。
在云世界中可用的解决方案包括 `SendGrid` 等服务;这取决于具体情况,我认为这是一个非常合理的解决方案。对于我的个人项目和测试,这确实是我的首选方法,因为我不用担心维护和支持 SMTP 服务器。然而在工作中,我们有现成的 SMTP 系统和一个专门的管理团队,所以我理想情况下需要一个解决方案,允许我直接发送电子邮件,就像我们在传统的 ASP.NET 4.x 应用程序中所做的那样。
和大多数编码挑战一样,我直接在 Google 上搜索,看看是否有人有相同的需求以及他们是如何解决这个问题的。然而,我没有找到像我预期的那么多有帮助的文档化解决方案。最终,我找到了 Github 上的 corefx 仓库中的这个 issue。这让我发现了 Jeffrey Stedfast 维护的 MailKit 库,事实证明它对我来说是一个很好的解决方案,因为它最近已更新为可以在 .NET Core 上工作。
在这篇文章中,我将带您完成我遇到的两种场景的实现过程。首先,通过 SMTP 中继直接发送邮件;其次,可以将电子邮件消息保存到 SMTP 拾取文件夹的可能性。这两种方法都比预期的要容易实现。
将 MailKit 添加到您的项目中
第一步是为 MailKit 添加 NuGet 包引用。我现在更喜欢直接使用 `project.json` 文件来设置我的依赖项。您需要在 `project.json` 文件中的依赖项部分添加 `MailKit` 库——在撰写本文时,它是 1.3.0-beta6 版本。
在标准的 ASP.NET Core Web 应用程序中,您的依赖项应如下所示:
保存更改后,VS 应该会触发对必要的 NuGet 包及其依赖项的还原。
通过 SMTP 服务器发送电子邮件
我在默认的 ASP.NET Core Web 应用程序项目中测试了这个解决方案,该项目已包含 `IEmailSender` 接口和一个 `AuthMessageSender` 类,只需实现即可。我选择使用这个类来测试实现是一个显而易见的选择,因为 DI 已经为它配置好了。在这篇文章中,我将展示发送电子邮件通过 SMTP 服务器入门所需的简要代码。
要继续学习,请打开您的 Web 应用程序项目中的 `MessageServices.cs` 文件。
我们需要在文件顶部添加三个 `using` 语句:
using MailKit.Net.Smtp;
using MimeKit;
using MailKit.Security;
现在可以像这样更新 `SendEmailAsync` 方法:
public async Task SendEmailAsync(string email, string subject, string message)
{
var emailMessage = new MimeMessage();
emailMessage.From.Add(new MailboxAddress("Joe Bloggs", "jbloggs@example.com"));
emailMessage.To.Add(new MailboxAddress("", email));
emailMessage.Subject = subject;
emailMessage.Body = new TextPart("plain") { Text = message };
using (var client = new SmtpClient())
{
client.LocalDomain = "some.domain.com";
await client.ConnectAsync("smtp.relay.uri", 25, SecureSocketOptions.None).ConfigureAwait(false);
await client.SendAsync(emailMessage).ConfigureAwait(false);
await client.DisconnectAsync(true).ConfigureAwait(false);
}
}
首先,我们声明一个 `new MimeMessage` 对象,它将代表我们将要发送的电子邮件消息。然后我们可以设置它的一些基本属性。
`MimeMessage` 有一个“from
”地址列表和一个“to
”地址列表,我们可以用我们的发件人和收件人填充它们。在此示例中,我为每个人添加了一个新的 `MailboxAddress`。`MailboxAddress` 的基本构造函数接受显示名称和邮箱的电子邮件地址。在我的例子中,“to
”邮箱接收由调用者传递到 `SendEmailAsync` 方法中的地址。
然后我们将主题字符串添加到电子邮件消息对象,然后定义正文。有几种方法可以构建消息正文,但现在我使用一种简单的方法来填充纯文本部分,使用传递到 `SendEmailAsync` 方法中的消息。如果需要,我们也可以为消息填充 HTML 正文。
这样我们就得到了一个非常简单的电子邮件消息对象,这里足以形成一个概念验证。最后一步是发送消息,为此我们使用 `SmtpClient`。请注意,这不是 `system.net.mail` 中的 `SmtpClient`,它是 `MailKit` 库的一部分。
我们实例化一个 `SmtpClient` 对象,并用 `using` 语句将其包装起来,以确保在使用完毕后将其处置掉。我们不想在发送完电子邮件后保持与 SMTP 服务器的连接开放。如果需要(我在我的代码中也这样做了),您可以设置与 SMTP 服务器通信时使用的 `LocalDomain`。这将作为电子邮件的来源显示。在我的例子中,我需要提供该域,以便我们的内部测试 SMTP 服务器能够接受并中继我的电子邮件。
然后我们异步连接到 SMTP 服务器。`ConnectAsync` 方法可以只接受 SMTP 服务器的 URI,或者像我在这里所做的那样,用端口和 SSL 选项进行重载。在我的情况下,当使用本地测试 SMTP 服务器进行测试时,不需要 SSL,所以我明确地指定了这一点以使其正常工作。
最后,我们可以异步发送消息,然后关闭连接。此时,电子邮件应该已经通过 SMTP 服务器发送出去了。
通过 SMTP 拾取文件夹发送电子邮件
如前所述,我还需要将消息放入 Web 服务器上运行的 SMTP 拾取文件夹,而不是直接通过 SMTP 服务器连接发送。也许有更好的方法可以做到这一点(我在测试中实现了,所以没有深入研究),但我最终做的是:
public async Task SendEmailAsync(string email, string subject, string message)
{
var emailMessage = new MimeMessage();
emailMessage.From.Add(new MailboxAddress("Joe Bloggs", "jbloggs@example.com"));
emailMessage.To.Add(new MailboxAddress("", email));
emailMessage.Subject = subject;
emailMessage.Body = new TextPart("plain") { Text = message };
using (StreamWriter data = System.IO.File.CreateText("c:\\smtppickup\\email.txt"))
{
emailMessage.WriteTo(data.BaseStream);
}
}
与我之前的代码唯一的真正区别是取消使用了 `SmtpClient`。相反,在生成我的电子邮件消息对象后,我创建了一个 `streamwriter`,它在本地目录中创建一个文本文件。然后我使用 `MimeMessage.WriteTo` 方法,传入基础流,以便在我的 `pickup` 目录中创建 RFC822 电子邮件消息文件。这会被 SMTP 系统拾取并发送。
总结
MailKit 似乎是一个很棒的库,它解决了我的即时需求。有迹象表明,微软团队将在某个阶段致力于移植自己的 SmtpClient 来支持 ASP.NET Core,但对于现在采纳/测试 .NET Core 的人来说,社区已经解决了这个问题,这真是太好了。