Microsoft Graph to Manage Mailboxes in C#

将 Microsoft Graph API 与 Aspose.Email for .NET 库集成的电子邮件应用程序使开发人员能够轻松访问和操作邮箱数据,执行诸如获取消息、检索文件夹层次结构和以不同格式保存电子邮件等操作。在本文中,我们将探讨如何利用这一强大的组合来处理和管理邮箱。

Microsoft Graph 演示

Microsoft Graph 是 Microsoft 提供的综合 API 平台,提供统一的端点以访问广泛的 Microsoft 服务和数据。它作为通向 Microsoft 365 中可用的庞大数据的门户,包括 Outlook 邮箱、日历、联系人、OneDrive、Teams 等等。

使用 Microsoft Graph,开发人员可以构建与用户数据和 Microsoft 云生态系统中的洞察无缝交互的应用程序。这是通过 RESTful API 和 SDK 来实现的,提供轻松进行身份验证、授权和查询数据的方法。

关于 Aspose.Email for .NET 库

Aspose.Email for .NET 是一个功能丰富的库,使开发人员能够在 .NET 应用程序中处理电子邮件文件和协议。它提供了一组强大的 API,用于创建、操作和转换各种格式的电子邮件消息,如 MSG、EML 和 MBOX。此外,该库支持 SMTP、POP3 和 IMAP 等电子邮件协议,使得电子邮件管理更加灵活。

在本指南中,我们将使用 Aspose.Email 与 Microsoft Graph 进行交互,以编程方式处理邮箱数据。借助 Aspose.Email 中的 GraphClient,我们可以高效地通过 Microsoft Graph 进行邮箱操作。

要开始使用该库,您需要将其集成到项目中。获取 Aspose.Email for .NET 的最简单方法是通过 NuGet 包管理器:

  • 在 Visual Studio 中打开您的项目。
  • 导航至工具 > NuGet 包管理器 > 管理解决方案的 NuGet 包。
  • 搜索 Aspose.Email
  • 选择该包并单击安装。

或者,您可以使用包管理器控制台:

Install-Package Aspose.Email

您还可以 直接从 Aspose 网站 下载 最新版本的 API。

在 Azure 门户上配置您的应用

在我们深入代码之前,配置 Azure 门户中的应用程序以启用 Microsoft Graph 访问至关重要。只需按照以下步骤操作:

  1. 创建 Azure Active Directory (AAD) 应用:

    • 导航至 Azure 门户
    • 转到 Azure Active Directory > 应用注册
    • 单击 新注册 创建新应用程序。
    • 提供名称并根据需要设置重定向 URI。
    • 单击 注册 完成过程。
  2. 设置 API 权限:

    • 在您的注册应用中,导航至 API 权限
    • 单击 添加权限 > Microsoft Graph
    • 选择 应用程序权限 进行服务器到服务器的调用。
    • 选择必要的权限,如 Mail.ReadMail.ReadWriteUser.Read 等。
    • 单击 添加权限 应用更改。
  3. 创建客户端密钥:

    • 转到应用的 证书和密钥
    • 单击 新客户端密钥 并提供描述。
    • 设置过期时间,然后单击 添加
    • 记录生成的客户端密钥值,因为您稍后需要它。
  4. 收集配置值:

    • 从应用的概述页面和密钥部分获取 租户 ID客户端 ID客户端密钥
    • 您将使用这些值进行身份验证并与 Microsoft Graph 进行交互。

一旦您的应用程序配置完成,您就可以进行开发任务。

获取访问令牌并初始化 GraphClient

在深入邮箱操作之前,我们需要打开通往 Microsoft Graph 广阔数据和服务生态系统的大门。通过 OAuth 2.0 访问令牌授予此访问权限——这是一个数字密钥,授权您的应用程序代表用户或服务与 Microsoft Graph 进行交互。可以将其想象为一个安全通行证,让您进入一个充满可能性的宇宙,从获取电子邮件到管理联系人,甚至更多。

第 1 步:设置配置文件

获取令牌的第一步是设置一个配置文件,该文件存储您的应用程序的 租户 ID客户端 ID客户端密钥 等基本信息。这些值是您应用程序的凭据,并在与 Microsoft 服务器通信时充当标识符。

以下是您的 JSON 配置可能的样子:

{
    "Instance": "https://login.microsoftonline.com/{0}",
    "ApiUrl": "https://graph.microsoft.com/.default",
    "TenantId": "YOUR_TENANT_ID_HERE",
    "ClientId": "YOUR_APP_ID_HERE",
    "ClientSecret": "YOUR_CLIENT_SECRET_HERE",
    "UserId": "YOUR_ID_HERE"
}

第 2 步:将 JSON 配置映射到 C# 对象

现在,让我们将此配置转换为应用程序可以使用的 C# 对象。我们将读取 JSON 文件并将其内容映射到 AuthenticationConfig 类,确保我们的应用程序知道在哪里可以找到所需的关键信息。

class AuthenticationConfig
{
    public string Instance { get; set; }
    public string ApiUrl { get; set; }
    public string TenantId { get; set; }
    public string ClientId { get; set; }
    public string UserId { get; set; }
    public string Authority => string.Format(CultureInfo.InvariantCulture, Instance, TenantId);
    public string ClientSecret { get; set; }
    
    public static AuthenticationConfig ReadFromJsonFile(string path)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile(path);

        var configuration = builder.Build();
        return configuration.Get<AuthenticationConfig>();
    }
}

第 3 步:获取访问令牌

配置到位后,是时候获取访问令牌了。我们将实现一个 GraphTokenProvider 类,使用 Microsoft 身份验证库 (MSAL) 处理身份验证过程。此类负责繁重的工作——与 Microsoft Graph 通信以获取授权我们应用程序的令牌。

class GraphTokenProvider : ITokenProvider
{
    private readonly IConfidentialClientApplication _app;
    private readonly string[] _scopes;
    private string? _token;
    
    public GraphTokenProvider(AuthenticationConfig config)
    {
        _app = ConfidentialClientApplicationBuilder.Create(config.ClientId)
            .WithClientSecret(config.ClientSecret)
            .WithAuthority(config.Authority)
            .Build();

        // 内存中的令牌缓存(应用和用户缓存)
        _app.AddInMemoryTokenCache();
        
        _scopes = new[] { config.ApiUrl }; 
    }

    public void Dispose()
    {
        throw new NotImplementedException();
    }

    public OAuthToken GetAccessToken()
    {
        return GetAccessToken(false);
    }

    public OAuthToken GetAccessToken(bool ignoreExistingToken)
    {
        if (!ignoreExistingToken && _token != null)
        {
            return new OAuthToken(_token);
        }

        _token = GetAccessTokenAsync().GetAwaiter().GetResult();
        return new OAuthToken(_token);
    }
    
    private async Task<string?> GetAccessTokenAsync()
    {
        AuthenticationResult? result;
        
        try
        {
            result = await _app.AcquireTokenForClient(_scopes)
                .ExecuteAsync();
            
            Console.WriteLine($"从 {result.AuthenticationResultMetadata.TokenSource} 获取的令牌 {Environment.NewLine}");
        }
        catch (MsalServiceException ex)
        {
            Console.WriteLine($"获取令牌错误:{Environment.NewLine}{ex}{Environment.NewLine}");
            result = null;
        }

        if (result == null) return null;
        _token = result.AccessToken;
        return result.AccessToken;
    }
}

第 4 步:初始化 GraphClient

最后,我们使用获取的令牌初始化 GraphClientGraphClient 作为与 Microsoft Graph 进行交互的桥梁,使我们能够无缝地与用户邮箱进行交互。

var config = AuthenticationConfig.ReadFromJsonFile("appsettings.json");
var tokenProvider = new GraphTokenProvider(config);

using var client = GraphClient.GetClient(tokenProvider, config.TenantId);
client.Resource = ResourceType.Users;
client.ResourceId = config.UserId;

获取文件夹层次结构并按名称检索文件夹

一旦您获得对 Microsoft Graph 的访问权限,就可以开始探索邮箱。在本节中,我们将深入探讨如何检索和导航邮箱的文件夹层次结构,使您能够按名称访问特定文件夹。

第 1 步:理解文件夹层次结构

导航文件夹结构

邮箱以层次结构的方式构建,像树一样。根文件夹分支出多个子文件夹,每个子文件夹包含自己的电子邮件集和可能更多的子文件夹。这种嵌套结构使得电子邮件的组织管理和导航变得简单。

让我们定义并使用 FolderNode 类来表示层次结构中的每个文件夹:

// 表示文件夹层次结构中的一个节点,扩展 FolderInfo 的属性并存储子文件夹的集合。
class FolderNode
{
    // 获取表示文件夹信息的 FolderInfo 对象。
    public FolderInfo Folder { get; }
    
    // 获取当前文件夹中包含的子文件夹集合。
    public List<FolderNode?> SubFolders { get; }

    // 用指定的 FolderInfo 对象初始化 FolderNode 类的新实例。
    public FolderNode(FolderInfo folder)
    {
        Folder = folder;
        SubFolders = new List<FolderNode?>();
    }
    
    // 以层次方式打印所有文件夹,从当前节点开始。
    public void PrintHierarchy()
    {
        PrintFolderNode(this, 0);
    }

    private void PrintFolderNode(FolderNode node, int indentLevel)
    {
        // 打印当前文件夹节点并缩进
        Console.WriteLine($"{new string(' ', indentLevel * 2)}{node}");

        // 递归打印子文件夹
        foreach (var subFolder in node.SubFolders)
        {
            PrintFolderNode(subFolder, indentLevel + 1);
        }
    }

    // 获取文件夹显示名称。
    public override string ToString()
    {
        return $"{Folder.DisplayName} ({Folder.ContentCount})";
    }
}

要检索完整的文件夹层次结构,我们将创建 FolderHierarchy 类,利用 GraphClient 递归列出所有文件夹。以下是其工作原理:

static class FolderHierarchy
{
    // 递归检索邮箱中的所有文件夹,并返回 FolderNode 对象的层次集合。
    public static List<FolderNode> Retrieve(IGraphClient client)
    {
        // 检索根文件夹
        var rootFolders = client.ListFolders();
        var allFolders = new List<FolderNode>();

        // 递归检索子文件夹
        foreach (var folder in rootFolders)
        {
            var folderNode = new FolderNode(folder);
            RetrieveSubFolders(client, folderNode);
            allFolders.Add(folderNode);
        }

        return allFolders;
    }

    // 递归检索子文件夹,并将其添加到父 FolderNode 的 SubFolders 属性中。
    private static void RetrieveSubFolders(IGraphClient client, FolderNode parentFolderNode)
    {
        if (parentFolderNode.Folder.HasSubFolders)
        {
            var subFolders = client.ListFolders(parentFolderNode.Folder.ItemId);
            
            foreach (var subFolder in subFolders)
            {
                var subFolderNode = new FolderNode(subFolder);
                RetrieveSubFolders(client, subFolderNode);
                parentFolderNode.SubFolders.Add(subFolderNode);
            }
        }
    }
}

第 2 步:检索文件夹层次结构

使用 FolderHierarchy.Retrieve 方法,您可以轻松遍历邮箱以发现其文件夹结构。以下是您可以如何实现这一点:

// 从邮箱中检索文件夹层次结构
var folderNodes = FolderHierarchy.Retrieve(client);

// 以结构化格式打印文件夹层次结构
foreach (var folderNode in folderNodes)
{
    folderNode.PrintHierarchy();
}

第 3 步:按名称检索文件夹

一旦检索到文件夹层次结构,您就可以按名称定位特定文件夹。无论您对访问收件箱还是任何自定义文件夹感兴趣,此方法确保您可以迅速找到它们:

// 指定您要查找的文件夹名称
string targetFolderName = "收件箱";

// 按名称查找目标文件夹
var targetFolder = folderNodes.FirstOrDefault(
    folderNode => folderNode.Folder.DisplayName.Equals(targetFolderName, StringComparison.OrdinalIgnoreCase))
    ?.Folder;

列出指定文件夹中的消息

成功检索文件夹层次结构后,下一步是深入特定文件夹的内容。想象一下,您已经导航到收件箱;现在,您想查看它所包含的所有消息。

在本节中,我们将探讨如何使用 GraphClient 和 Aspose.Email for .NET 列出特定文件夹中的消息。

一旦您有了文件夹,在其中列出消息是直接的。GraphClient 提供了一个 ListMessages 方法来检索文件夹中的所有消息,您可以处理或显示它们。

以下是从指定文件夹列出消息的代码:

Console.WriteLine("列出指定文件夹中的消息...");

// 调用客户端方法列出所选文件夹中的消息
var messageInfoCollection = client.ListMessages(targetFolder.ItemId);

Console.WriteLine($"{targetFolderName}:");

// 打印消息的主题行
foreach (var messageInfo in messageInfoCollection)
{
    Console.WriteLine($"     - {messageInfo.Subject}");
}

messageInfoCollection 包含有关每封电子邮件的基本信息。此信息可用于显示摘要、生成报告,甚至根据特定条件触发警报。

结论

在本文中,我们探讨了如何利用 Microsoft Graph 和 Aspose.Email for .NET 库有效处理邮箱、导航文件夹层次结构以及列出特定文件夹中的消息。通过遵循这些步骤,您可以构建强大的应用程序,与电子邮件数据无缝交互,从而提供增强的功能和用户体验。

概念实践

如果您渴望看到这些概念的实际应用,可以下载一个完整的示例应用程序。该应用程序包含本文中描述的源代码,并演示如何逐步实现这些功能。

访问我们的 GitHub 仓库以获取示例应用程序:Aspose.Email for .NET - GraphApp 示例

通过 Microsoft Graph 处理邮箱提供了对电子邮件数据和管理能力的无与伦比的访问。通过正确的工具和技术,您可以构建复杂的应用程序,提供有意义的洞察并自动化复杂任务,最终提升生产力和用户满意度。

免费试用

此外,Aspose.Email 提供全面的 文档、广泛的 API 参考 和各种免费的在线工具和 应用 来增强您的开发过程。开发人员还可以访问免费的 支持论坛 以获取社区帮助和见解,并通过 Aspose 博客 了解最新的技巧和教程。

另请参见