如何从WinForms阅读收件箱Outlook.com邮件?

情景


我想用WinForms技术开发一个用C#或Vb.Net编写的非常简单的应用程序,它将帮助我自动执行一个简单的任务,包括访问我的Outlook.com帐户以阅读从Youtube收到的电子邮件然后解压缩videourl。

问题


我的网络相关知识并不好,我陷入了最重要的一点,试图找到最简单的方法来完成这项任务(我的意思是.Net或第三方API的官方微软API或其他方式可以做到这一点),尝试应用所需的OAuth2 autorizathion来访问电子邮件帐户。

我知道下面的代码没有集中在正确的方向,因为缺乏授权,但我不知道如何实现既不如何阅读电子邮件,所以这是我尝试的:

string url = "https://outlook.office.com/api/v2.0/me/messages"; string result = ""; HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url); req.Method = "GET"; try { using (WebClient wc = new WebClient()) { wc.Encoding = Encoding.UTF8; result = wc.DownloadString(url); } } catch (Exception ex) { MessageBox.Show(ex.Message); } 


我如何访问我的Outlook.com帐户以阅读我拥有的收件箱电子邮件的标题和内容? 而且,除了可选择响应(只有在可能的情况下并且没有太多问题),我如何删除电子邮件?

请注意,此问题是指Outlook.com在线服务,而不是Outlook的桌面客户端,也不是他们的COM库或Office365的使用

答案要求


我知道我没有人要求帮助并提出一些必要条件,所有的帮助对我都很感激,但这次我需要特别要求,因为我的头脑疯狂试图理解,使用和改编OAuth2从头开始制作的解决方案,它生成了很长的代码,我根本不理解,这对我来说太过分了。

出于这个原因,只有当提供的解决方案基于第三个pary库的使用时才会接受这个问题的答案,因为它将作为OAuth2实现的完整抽象,如RestSharpCodeScalesDotNetOpenAuth ,或任何其他(免费)lib将为我处理所需的东西,而不是自己从头开始开发OAuth2算法。

研究


我的调查开始阅读微软的Outlook开发。 页面,关于 Mail API参考, REST API文档, Outlook.com API,此类入门文章,并以此完全说明性示例结束使用ASP.Net。

我从微软的文章中清楚地看到的只是……没有什么,我注册了应用程序并创建了客户端ID和秘密,但微软没有为Winforms提供任何示例,所以我试图翻译他们的官方ASP.NET示例到WinForms没有成功。

在浪费时间之后,我找到了这个 OAuth文档页面,它提供了一些.NET,我想这将有助于OAuth2授权,然后我发现了DotNetOpenAuth库看起来非常完整,我也发现了使用DotNetOpenAuth的 Outlook代码示例但同样是ASP.NET ,以及这些通用和官方DotNetOpenAuth的代码示例,但我找不到任何可以帮助我做我想做的事情。

一般的想法是遵循这里的教程: 开始使用邮件,日历和联系人REST API,但我将尝试简化它并使用Winforms示例演示它。

注册应用程序

首先,您需要创建应用程序注册门户并将其注册到应用程序注册门户(对于给定的应用程序,您只需要执行一次此操作):

  1. 创建一个应用程序
  2. 生成一个新密码
  3. 添加一个平台,然后选择移动(这里的“移动”意味着“任何设备”或“不适用于浏览器”……)
  4. 别忘了点击保存!

认证

现在,在您的“任何设备”代码(包括winforms)中,您需要进行身份validation。 最简单的方法是使用ADAL(“Active Directory身份validation库”)。 源代码可在此处获得https://github.com/AzureAD/azure-activedirectory-library-for-dotnet ,二进制文件可作为nuget使用。

不幸的是,今天你可以在nuget上获得的最新版本名为Microsoft.IdentityModel.Clients.ActiveDirectory对我不起作用(它有一个我在这里报告的错误https://github.com/AzureAD/azure-activedirectory-library -for-dotnet / issues / 412已经修复,但现在服务器抱怨某些应用程序与服务器不兼容)。

所以你必须使用旧的“实验性”(在将来的某一天记住这一点,我们将不得不切换): Microsoft.Experimental.IdentityModel.Clients.ActiveDirectory

您希望确保在获取身份validation令牌时使用正确的范围。 范围在此处定义: Outlook邮件,日历和联系人范围 ,表示权限区域。 如果没有指定范围,除了进行身份validation之外,您无需执

因此,如果您想阅读邮件,请使用“ https://outlook.office.com/mail.read ”范围。

当您尝试应用程序时,在身份validation对话框之后,它应该向用户显示同意屏幕(这里我们看到我们要求邮件范围:“读取您的邮件”):

在此处输入图像描述

Office / Outlook / OData API

一旦身份validation工作,您可以直接使用REST api,这不是那么容易,或者是懒惰并使用另一个包: Microsoft.Office365.OutlookServices-V2.0将为您完成所有底层REST / OData魔术。 好消息是这个API非常完整,所以它应该允许你做其他的事情,比如消息创建,删除等。

outlook.com案例有一个重要的注意事项:并非所有帐户都为此整个REST API启用(请查看此处的“REST API可用性”一章: Outlook Mail ),因此您可能需要创建一个新的用于测试的帐户。

以下是示例应用程序的winforms代码,该应用程序将查询10条消息并将其添加到列表框中。

 using System; using System.Threading.Tasks; using System.Windows.Forms; using Microsoft.Experimental.IdentityModel.Clients.ActiveDirectory; using Microsoft.Office365.OutlookServices; namespace WindowsFormsApplication1 { public partial class Form1 : Form { private const string authority = "https://login.microsoftonline.com/common"; private const string clientId = "blablabl-abla-blab-abla-blablablab"; // TODO: put your application id here private const string redirectUri = "urn:ietf:wg:oauth:2.0:oob"; // put your redirect uri here (should be the same) // we cache the token for the duration of this form // you could/should use the FileCache class provided in the sample here https://dev.outlook.com/restapi/tutorial/dotnet private TokenCache _cache = new TokenCache(); public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { // since all packages force async, // we have to avoid threading issues BeginInvoke((Action)(() => GetMessages())); } private async void GetMessages() { // use the Microsoft.Experimental.IdentityModel.Clients.ActiveDirectory nuget package for auth var authContext = new AuthenticationContext(authority, _cache); var result = await authContext.AcquireTokenAsync( new[] { "https://outlook.office.com/mail.read" }, null, clientId, new Uri(redirectUri), new PlatformParameters(PromptBehavior.Always, this)); // use the Microsoft.Office365.OutlookServices-V2.0 nuget package from now on var client = new OutlookServicesClient(new Uri("https://outlook.office.com/api/v2.0"), () => Task.FromResult(result.Token)); var messages = await client.Me.Messages .Take(10) // get only 10 messages .ExecuteAsync(); // fill some list box // (beware, some messages have a null subject) foreach (var msg in messages.CurrentPage) { listBox1.Items.Add(msg.Subject); } } } } 

我只想分享(几乎)最终的解决方案,我扩展了@ Simon Mourier提供的解决方案,以迭代特定文件夹的所有电子邮件,如果电子邮件来自Youtube,则废弃其中的url,然后收回电子邮件。

什么时候应用这个?,好吧,只需针对您需要解析电子邮件的任何情况进行调整,我的情况非常具体,我有大约500个频道订阅,所以我在一个月内从Youtube积累了大约200封电子邮件,大部分是电子邮件来自音乐频道我刚刚阅读电子邮件以复制url以使用JDownloader下载它,所以这对于救助者很有用,因为它将为我完成所有任务。

 Private Const Authority As String = "https://login.microsoftonline.com/common" Private Const ClientId As String = "OUR API ID" ' Put your redirect uri here (should be the same). Private Const RedirectUri As String = "urn:ietf:wg:oauth:2.0:oob" ' We cache the token for the duration of this Form. ' You could/should use the FileCache class provided in the sample here: ' https://dev.outlook.com/restapi/tutorial/dotnet Private cache As New TokenCache() Private Sub Form1_Shown() Handles MyBase.Shown ' Since all packages force async, we have to avoid threading issues. Me.BeginInvoke(Sub() GetMessages()) End Sub Private Async Sub GetMessages() ' Use the 'Microsoft.Experimental.IdentityModel.Clients.ActiveDirectory' Nuget package for auth. Dim authContext As New AuthenticationContext(Authority, cache) Dim result As AuthenticationResult = Await authContext.AcquireTokenAsync({"https://outlook.office.com/mail.readwrite"}, Nothing, ClientId, New Uri(RedirectUri), New PlatformParameters(PromptBehavior.Auto, Me)) ' Use the 'Microsoft.Office365.OutlookServices-V2.0' Nuget package from now on. Dim client As New OutlookServicesClient(New Uri("https://outlook.office.com/api/v2.0"), Function() Task.FromResult(result.Token)) ' I have a rule set to automatically move all emails received from Youtube to a folder with name "Youtube". Dim folder As IMailFolder = Await client.[Me].MailFolders.Where(Function(f As IMailFolder) f.DisplayName = "Youtube").ExecuteSingleAsync() Dim messages As IPagedCollection(Of IMessage) = Await client.[Me].MailFolders.GetById(folder.Id).Messages.ExecuteAsync() Do While True Me.ParseYoutubeMessages(messages.CurrentPage) If messages.MorePagesAvailable Then messages = Await messages.GetNextPageAsync Else Exit Do End If Loop End Sub Private Async Sub ParseYoutubeMessages(ByVal messageList As IReadOnlyList(Of IMessage)) Dim urlRegex As New Regex("""http://www.youtube.com/.+watch.+uploademail""", RegexOptions.IgnoreCase) For Each msg As IMessage In messageList If (msg.From.EmailAddress.Name.Equals("YouTube", StringComparison.OrdinalIgnoreCase)) Then Dim body As String = msg.Body.Content Dim isMatch As Boolean = urlRegex.IsMatch(body) If Not (isMatch) Then Throw New InvalidOperationException("Youtube url regex doesn't match.") Else Dim urlMatches As MatchCollection = urlRegex.Matches(body) Dim urls As String() = (From m As Match In urlMatches.Cast(Of Match) Select Environment.NewLine & m.Value).Distinct().ToArray() File.AppendAllText("C:\Youtube Urls.txt", String.Join("", urls)) msg.IsRead = True Await msg.MoveAsync("DeletedItems") End If End If Next msg End Sub 

更新为2018年 – Microsoft Graph API现在可以控制授予对365世界中对象的访问权限,包括电子邮件。 有关4个步骤的概述,请参阅此链接: https : //developer.microsoft.com/en-us/graph/docs/concepts/auth_v2_user