c# - Microsoft.Graph SDK 以用户身份发送邮件 - 400 - 不支持意外异常或打开导航属性
问题描述
我正在开发一个应用程序,该应用程序需要在用户完成操作时向他们发送电子邮件通知和提醒。用户提交数据,然后应用程序通知其他用户按特定顺序执行操作(即用户 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 下工作,所以我仍在寻找原始问题的答案。
解决方案
在某些时候,我已将客户端的 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();
推荐阅读
- c# - 使用 C# 的交互式输入?
- javascript - FirebaseUI 登录完成后,Firebase 令牌如何传递到重定向 url?
- python - 如何在python中导入opencv?
- node.js - aws FIFO 队列返回空队列,即使它有可用的消息
- java - ListView 上的 JavaFX RemoteException?
- javascript - 使用javascript计算单词 - 邮递员测试
- python - opencv中文件的路径应该是什么
- swift - 为什么 Swift JSONDecodable 失败了?
- php - 具有多个逻辑运算符(AND、OR)的嵌套 IF 条件
- php - 为什么“//localhost/mysite/config/settings.yml”需要大约 30 秒的 realpath()?