1

我正在开发一个应用程序,该应用程序需要在用户完成操作时向他们发送电子邮件通知和提醒。用户提交数据,然后应用程序通知其他用户按特定顺序执行操作(即用户 1:任务 1,任务 1 完成后,用户 2:任务 2,等等) - 如果用户花费的时间过长执行他们的操作,系统会提醒他们,然后服从他们的经理(通过 Windows 服务或类似服务)。因此,我无法代表当前登录的用户发送消息 - 它需要能够自行发送消息。最好代表提交数据的用户发送,以便后续用户可以直接回复他们。

我正在使用Microsoft Graph 客户端库v1.10.0。运行我的代码会产生一个聚合异常,最终归结为代码 400, code "generalException", message"Unexpected exception returned from the service." 我使用 LinqPad 来查看 Graph 对象,并尝试在 Postman 中重现调用,这会产生 400 和消息"Open navigation properties are not supported on OpenTypes. Property name: 'microsoft.graph.sendmail'."

更详尽的细节:

  • 应用程序具有 Microsoft Graph -> Send mail as any user, Read all groups,Read all users' full profiles权限。
  • 调用会GraphServiceClient.Client.Users["MyUPN"].SendMail(email, true).Request().PostAsync()产生 400 一般异常Unexpected exception returned from the service.(下面的完整代码)
  • 查看请求,我发现它正在调用https://graph.windows.net:443/{{tenantId}}/users/{{MyUPN}}/microsoft.graph.sendMail?api-version=1.6并尝试通过 Postman(使用有效令牌)进行相同的调用,这产生了 400 错误请求和消息Open navigation properties are not supported on OpenTypes. Property name: 'microsoft.graph.sendMail'.

完整代码:

String MyEmailAddress = "";
String MyUpn = "";
String TenantId = "";
String AppGuid = "";
String AppKey = "";

var sender = new Microsoft.Graph.Recipient()

{
    EmailAddress = new Microsoft.Graph.EmailAddress() { Address = MyEmailAddress }
};
var email = new Microsoft.Graph.Message
{
    Sender = sender,
    From = sender,
    Subject = "Test",
    Body = new Microsoft.Graph.ItemBody()
    {
        Content = "Test Body",
        ContentType = Microsoft.Graph.BodyType.Text
    }
};

email.ToRecipients = new List<Microsoft.Graph.Recipient>(){ sender };

email.BodyPreview = "Test Summary";


GraphSdk _Sdk = new GraphSdk(TenantId, AppGuid, AppKey);

// Where the error throws
await _Sdk.Client.Users[MyUpn].SendMail(email, true).Request().PostAsync();

作为测试,我也尝试await _Sdk.Client.Users[MyUpn].Messages.Request().Top(20).GetAsync();了同样的错误。其他 Graph 调用,例如获取用户的组或经理,工作正常 - 此错误仅出现在与电子邮件相关的调用中。


2018 年 9 月 19 日上午更新

如果我使用证书而不是密钥->密码来生成令牌,看起来我可以让电子邮件正常工作;并改为调用 Outlook API。不幸的是,这不适用于 GraphServiceClient 和 Graph API - 它可以使用证书,并使用 Outlook API 基本 URL,但该microsoft.graph.sendMail操作仅sendMail在 Outlook API 中进行。

为了可维护性,我仍然想让它在 Graph API 下工作,所以我仍在寻找原始问题的答案。

4

2 回答 2

1

在某些时候,我已将客户端的 BaseUrl 设置为https://graph.windows.net:443/{{tenantId}},这可能是由于过去几年的不同品牌(Microsoft Graph 与 Azure Graph)。根据 Microsoft.Graph 的当前建议,它应该是https://graph.microsoft.com/v1.0/- 这似乎也是默认值。

此外,我不得不切换到使用证书而不是 Azure 生成的密钥 -> 应用程序密码。

总工作代码是:

String AADTenantId = "";
String AppGuid = "";
String SenderAddress = "";
String SenderId = "";
String ToAddress = "";
String SubjectText = "";
String BodyText = "";
Byte[] Certificate = ...GetCertBytes...
String CertPassword = "";

var client = new GraphServiceClient(new DelegateAuthenticationProvider(
    async requestMessage =>
    {
        var authContext = new AuthenticationContext($"https://login.microsoftonline.com/{AADTenantId}");
        var cert = new X509Certificate2(Certificate, CertPassword);
        var clientAssertion = new ClientAssertionCertificate(AppGuid, cert);
        AuthenticationResult authresult = await authContext.AcquireTokenAsync("https://graph.microsoft.com", clientAssertion);

        // Append the access token to the request
        requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authresult.AccessToken);
    }));

var sender = new Recipient()
{
    EmailAddress = new EmailAddress() { Address = SenderAddress }
};
var email = new Message
{
    Sender = sender,
    From = sender,
    Subject = SubjectText,
    Body = new ItemBody()
    {
        Content = BodyText,
        ContentType = BodyType.Text
    },
    ToRecipients = new List<Recipient>() {
        new Recipient() { EmailAddress = new EmailAddress { Address = ToAddress }}
    }
};

await client.Users[SenderId].SendMail(email, true).Request().PostAsync();
于 2018-09-19T14:47:17.577 回答
0

根据您的描述,您想发送电子邮件但收到 400 错误。

根据我的测试,我们可以使用以下步骤发送电子邮件。

step1,我们应该得到一个graphClient经过身份验证的HttpClient。

像这样的代码:

GraphServiceClient graphServiceClient = new GraphServiceClient(
            new DelegateAuthenticationProvider(
                async (requestMessage) =>
                {
                    string accessToken = await MsalAuthProvider.Instance.GetUserAccesstokenAsync();
                    requestMessage.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", accessToken);
                }));
        return graphServiceClient;

我们可以参考官方文档中的简单代码

step2,我们可以使用以下代码发送邮件:

public async Task<bool> SendAsync(EmailAddress toaddress)
    {
        var email = new Message
        {
            Body = new ItemBody
            {
                Content = "Test for sending eamil ",
                ContentType = BodyType.Text,
            },
            Subject = "Test for sending eamil",
            ToRecipients = new List<Recipient>
            {
                new Recipient
                {
                   EmailAddress = toaddress 
                }
            },
        };
        try
        {
            await _serviceClient.Me.SendMail(email).Request().PostAsync(); // the _serviceClient is the result in the step1.
            return true;
        }
        catch (Exception ex)
        {
            return false;
        }
于 2018-09-20T05:31:02.990 回答