65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Office 365 创建 ASP.NET Web API Web 服务以发送已身份验证的电子邮件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (3投票s)

2015年8月21日

CPOL

7分钟阅读

viewsIcon

48410

如何使用 ASP.NET Web API 创建一个集中的 Web 服务,通过 Office 365 发送已身份验证的电子邮件

引言

您可以将 Office 365 用作应用程序的出站 SMTP 服务器,如果您正在使用它向 Office 365 上的受权限限制的分发列表发送邮件,则您的代码除了简单地设置 Mailmessage 对象中的 From 字段之外,还需要对发送邮件的用户帐户进行身份验证。

本文将演示以下几点

  • 如何连接和使用 Office 365 SMTP 服务器从您的应用程序发送电子邮件
  • 如何通过 Office 365 进行身份验证,以便您可以向受权限限制的分发列表发送邮件
  • 如何将代码实现为通过 ASP.Net Web API 的共享 Web 服务,以便轻松重用

背景

最近,我们已将组织电子邮件服务切换到 Office 365,并停用了接受匿名入站 SMTP 邮件的旧内部 SMTP 服务器。以前,需要发送广播电子邮件的应用程序只需向内部 SMTP 服务器发送一封匿名电子邮件,它就会发送到指定分发列表中的用户。但是,Office 365 的受权限限制的分发列表要求发送邮件的帐户在允许将邮件发送到相应分发列表之前进行身份验证。

为了简化我多年来开发的众多应用程序的代码更改过渡,我决定实现一个集中的 Web 服务来封装已身份验证电子邮件的发送逻辑,以便我们可以以标准化的方式轻松重用代码。

本文还将演示如何通过一个小的 C# 控制台应用程序,通过简单的 REST POST 请求连接到 Web 服务,但是,它可以轻松地适应需要发送已身份验证电子邮件的任何语言。如果您需要您语言的客户端代码示例,请给我发一封电子邮件。

软件先决条件

  • .NET Framework 4.5 及以上版本的 Visual Studio 2015(或 Visual Studio 2013)
  • Office 365 帐户

如果您没有 Visual Studio 2015 或 Visual Studio 2013 的完整版本,您可以使用微软网站上可下载的免费 Visual Studio 2015 Community 版本。

设置 ASP.NET Web API 项目

我们需要在 Visual Studio 中设置一个新的 ASP.NET Web API 项目,以便稍后在某个路由操作中添加发送电子邮件的代码。启动 Visual Studio 并选择一个新的 ASP.NET Web 应用程序。我们可以在下一个屏幕中选择 Web API 模板。

在下一个屏幕中,选择 Web API 模板。选择此模板后,Visual Studio 会方便地为我们创建文件夹来组织代码。如果您使用的是 Visual Studio 2015,请记住取消选中“在云中托管”选项,并且为了简化此演示和可重用性,让我们将身份验证更改为“无身份验证”。

创建我们的电子邮件控制器

让我们创建一个新的控制器来处理我们的电子邮件发送代码。右键单击Controllers文件夹并添加一个新的“Web API 2 Controller - Empty”,因为此示例不需要数据库访问。

ASP.NET Web API 通过路由解析要激活的方法,该路由在 App_Start > WebApiConfig.cs 中指定。让我们为我们的新电子邮件发送方法指定一个名称,以便我们的客户端应用程序可以轻松调用它。

[HttpPost]
[ActionName("sendmail")]
public IHttpActionResult processAuthEmail()
{  
}

在上面的代码示例中,我们将我们的操作命名为“sendmail”。在我们的 POST 请求中,我们将使用对“sendmail”操作发出请求,这将导致 ASP.NET Web API 将请求路由到此方法。我们还使用“HttpPost”属性将此方法标记为仅响应 POST 请求。

通过 Office 365 进行身份验证并发送电子邮件

在我们的示例中,我将展示如何为您的邮件对象添加不同类型的邮件选项,如抄送、回复到和附件。首先,我们需要通过使用模型来指定将通过服务器 POST 的数据类型。此模型允许您指定客户端请求中将发布的字段。

在解决方案资源管理器中,右键单击 Models 并选择“New Class”。让我们将新类命名为“SendMailRequest.cs”。

在类中,我们将放置通常与邮件消息相关的属性。除了通用属性外,您还可以包含任何自定义属性,您希望您的客户端在服务器上进行处理时发送。

public class SendMailRequest
{
    public string recipient { get; set; }
    public string cc { get; set; }
    public string replyto { get; set; }
    public string subject { get; set; }
    public string body { get; set; }
    public string filecontent { get; set; }
    public string filename { get; set; }
}

切换回我们的 EmailController 类,让我们包含 Models 路径,以便控制器知道如何引用我们刚刚创建的新类。在 EmailController.cs 的顶部,添加一个指向 Models 路径的“using”语句。

using SendAuthMailService.Models;

我们现在准备编写 Office 365 身份验证和发送电子邮件的代码,但在那之前,我们需要确保客户端的 POST 参数已自动映射到我们刚刚指定的 SendMailRequest 类的属性,并且这些参数已传递到我们的方法。

为此,请修改“processAuthEmail”方法以传入一个 SendMailRequest 对象。

public IHttpActionResult SendEmail(SendMailRequest mailModel)

现在我们可以使用通过“mailModel”对象传递到方法中的属性了。

首先,我们定义一个新的 MailMessage 对象,以便我们可以对其设置邮件选项。

System.Net.Mail.MailMessage msg = new MailMessage();

接下来,我们可以处理传递到方法中的数据。让我们填充收件人。

// Separate the recipient array
    string[] emailAddress = mailModel.recipient.Split(',');

    foreach (string currentEmailAddress in emailAddress) { 
        msg.To.Add(new MailAddress(currentEmailAddress.Trim()));
    }

接下来,让我们填充 Cc 字段和 Reply-to 字段。这些字段是可选的,因此我们需要检查它们是否已在传递到方法中的模型中定义。如果您希望广播电子邮件的回复发送给某人,Reply-To 字段非常重要。

// Separate the cc array , if not null
    string[] ccAddress = null;

    if (mailModel.cc != null)
    {
        ccAddress = mailModel.cc.Split(',');
        foreach (string currentCCAddress in ccAddress)
        {
            msg.CC.Add(new MailAddress(currentCCAddress.Trim()));
        }
    }

    // Include the reply to if not null
    if (mailModel.replyto != null)
    {
        msg.ReplyToList.Add(new MailAddress(mailModel.replyto));
    }

最后,在我们的示例代码中,我们添加了向出站电子邮件添加可选附件的功能。请记住,我们的模型指定了两个字符串 - filename 和 filecontent,其中 filename 是客户端发送的文件名,filecontent 是文件的 Base64 表示。我们将在本文末尾展示一个客户端示例。请注意,我们还包含了一个临时目录,我们用它来从 base64 字符串组装文件。

// Include the file attachment if the filename is not null
    if (mailModel.filename != null)
    {
        // Declare a temp file path where we can assemble our file
        string tempPath = Properties.Settings.Default["TempFile"].ToString();

        string filePath = Path.Combine(tempPath, mailModel.filename);

        using (System.IO.FileStream reader = System.IO.File.Create(filePath))
        {
            byte[] buffer = Convert.FromBase64String(mailModel.filecontent);
            reader.Write(buffer, 0, buffer.Length);
            reader.Dispose();
        }

        msg.Attachments.Add(new Attachment(filePath));

    }

在将各种邮件选项分配给我们的 MailMessage 对象后,我们现在可以设置邮件消息的发送者并 against Office 365 服务器进行身份验证以发送我们的邮件。下面的代码显示了如何完成此操作。

string sendFromEmail = Properties.Settings.Default["SendFromEmail"].ToString();
string sendFromName = Properties.Settings.Default["SendFromName"].ToString();
string sendFromPassword = Properties.Settings.Default["SendFromPassword"].ToString();

msg.From = new MailAddress(sendFromEmail , sendFromName);
    msg.Subject = mailModel.subject;
    msg.Body = mailModel.body;
    msg.IsBodyHtml = true;
    SmtpClient client = new SmtpClient("smtp.office365.com");
    client.Port = 587;
    client.EnableSsl = true;
    client.UseDefaultCredentials = false;
    NetworkCredential cred = new System.Net.NetworkCredential(sendFromEmail , sendFromPassword);
    client.Credentials = cred;
    client.DeliveryMethod = SmtpDeliveryMethod.Network;
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;

我们将发送操作包装在 try 和 catch 异常块中以处理任何意外错误。请注意,在发送完电子邮件后,如果指定了附件,我们还会删除在临时文件夹中组装的文件。

try
  {
        client.Send(msg);
        msg.Dispose();

        // Clean up the temp directory if used
        if (mailModel.filename != null)
        {
            string tempPath = Properties.Settings.Default["TempFile"].ToString();
            string filePath = Path.Combine(tempPath, mailModel.filename);
            File.Delete(filePath);
        }

        return Ok("Mail Sent");

    }
    catch (Exception e)
    {
        // Handle errors here
    }

最后,我们将一个属性文件添加到项目中,以便我们可以指定各种应用程序属性,如 TempFileSendFromNameSendFromEmail 等。

要添加属性文件,请在解决方案资源管理器中右键单击项目并选择“Properties”。在新打开的选项卡中,单击“Settings”。然后,Visual Studio 会通知您尚未设置默认设置文件。单击链接以创建默认设置文件。

输入此应用程序所需的所有设置,TempFile 设置的示例如下所示

将所有内容放在一起,完成的代码如下所示

[HttpPost]
    [ActionName("sendmail")]
    public IHttpActionResult processAuthEmail(SendMailRequest mailModel)
    {

        // Send the email
        System.Net.Mail.MailMessage msg = new MailMessage();

        // Separate the recipient array
        string[] emailAddress = mailModel.recipient.Split(',');

        foreach (string currentEmailAddress in emailAddress)
        {
            msg.To.Add(new MailAddress(currentEmailAddress.Trim()));
        }

        // Separate the cc array , if not null
        string[] ccAddress = null;

        if (mailModel.cc != null)
        {
            ccAddress = mailModel.cc.Split(',');
            foreach (string currentCCAddress in ccAddress)
            {
                msg.CC.Add(new MailAddress(currentCCAddress.Trim()));
            }
        }

        // Include the reply to if not null
        if (mailModel.replyto != null)
        {
            msg.ReplyToList.Add(new MailAddress(mailModel.replyto));
        }

        // Include the file attachment if the filename is not null
        if (mailModel.filename != null)
        {
            // Declare a temp file path where we can assemble our file
            string tempPath = Properties.Settings.Default["TempFile"].ToString();

            string filePath = Path.Combine(tempPath, mailModel.filename);

            using (System.IO.FileStream reader = System.IO.File.Create(filePath))
            {
                byte[] buffer = Convert.FromBase64String(mailModel.filecontent);
                reader.Write(buffer, 0, buffer.Length);
                reader.Dispose();
            }

            msg.Attachments.Add(new Attachment(filePath));

        }

        string sendFromEmail = Properties.Settings.Default["SendFromEmail"].ToString();
        string sendFromName = Properties.Settings.Default["SendFromName"].ToString();
        string sendFromPassword = Properties.Settings.Default["SendFromPassword"].ToString();

        msg.From = new MailAddress(sendFromEmail, sendFromName);
        msg.Subject = mailModel.subject;
        msg.Body = mailModel.body;
        msg.IsBodyHtml = true;

        SmtpClient client = new SmtpClient("smtp.office365.com");
        client.Port = 587;
        client.EnableSsl = true;
        client.UseDefaultCredentials = false;
        NetworkCredential cred = new System.Net.NetworkCredential(sendFromEmail, sendFromPassword);
        client.Credentials = cred;
        client.DeliveryMethod = SmtpDeliveryMethod.Network;
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;

        try
        {
            client.Send(msg);
            msg.Dispose();

            // Clean up the temp directory if used
            if (mailModel.filename != null)
            {
                string tempPath = Properties.Settings.Default["TempFile"].ToString();
                string filePath = Path.Combine(tempPath, mailModel.filename);
                File.Delete(filePath);
            }

            return Ok("Mail Sent");
        }
        catch (Exception e)
        {
            return NotFound();
        }

    }

用于发送电子邮件的 C# 客户端代码示例

由于我们将 ASP.Net Web API 实现为标准的 REST 服务,我们可以通过 POST 来激活它。我们的 Web 服务的默认 URL 路由是“/api/{controllername}/{actionname}”,在本例中是“email”。

客户端的示例代码如下

 WebClient client = new WebClient();
     string SERVERPATH = "YOUR SERVER PATH";   
    NameValueCollection valCollection = new NameValueCollection();
    valCollection.Add("recipient", "test@test.com");
    // valCollection.Add("cc", "anothertest@test.com"); // Uncomment if you have a CC party
    valCollection.Add("subject", "Test Subject");
    valCollection.Add("body", "Test Body");
    // valCollection.Add("filename", "myfile.txt");
    // valCollection.Add("filecontent", Serialize("C:\\MyOriginalFile.txt")); // Uncomment if you want to send a file across

    client.UploadValues(SERVERPATH + "/api/email/sendmail", valCollection);

    Console.WriteLine("Done");

便利的 Serialize 方法的示例代码

我们将以 Base64 字符串的形式发送我们的文件。以下是您可以使用此代码将文件转换为 Base64 字符串。

public static string Serialize(string fileName)
    {
        using (FileStream reader = new FileStream(fileName, FileMode.Open))
        {
            byte[] buffer = new byte[reader.Length];
            reader.Read(buffer, 0, (int)reader.Length);
            return Convert.ToBase64String(buffer);
        }
    }

请告诉我它对您来说效果如何!

© . All rights reserved.