Microsoft.Graph SDK SendMail как пользователь - 400 - Неожиданные исключения или открытые свойства навигации не поддерживаются

Я работаю над приложением, которое должно отправлять уведомления по электронной почте и напоминания пользователям, когда у них есть действия, которые необходимо выполнить. Пользователь отправляет данные, затем приложение уведомляет других пользователей о выполнении действий в определенном порядке (т. Е. Пользователь 1: Задача 1, после завершения Задачи 1, Пользователь 2: Задача 2 и т. Д.) - если пользователю требуется слишком много времени для выполнить их действие, система будет напоминать им, а затем передать их своему менеджеру (через службу Windows или подобное). Из-за этого я не могу отправлять сообщения от имени текущего вошедшего в систему пользователя - он должен иметь возможность отправлять сообщения самостоятельно. Предпочтительно отправлять сообщения от имени пользователя, отправившего данные, чтобы последующие пользователи могли отвечать непосредственно им.

Я использую клиентскую библиотеку Microsoft Graph v1.10.0. Выполнение моего кода дает статистическое исключение, в конечном итоге сводящееся к коду 400, код "generalException"сообщение "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 и попытался сделать тот же звонок через почтальона (с действительным токеном), который выдал 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, такие как получение групп пользователей или менеджера, работают нормально - эта ошибка появляется только при вызовах, связанных с электронной почтой.


Обновление 9/9/2018 утра

Похоже, что я могу заставить работать электронную почту, если я использую сертификат для генерации токена вместо Ключа -> Пароль; и вместо этого вызовите Outlook API. К сожалению, это не работает через GraphServiceClient и Graph API - он может использовать сертификат и использовать базовый URL-адрес Outlook API, но microsoft.graph.sendMail действие просто sendMail в API Outlook.

Для удобства обслуживания я все же хотел бы, чтобы все это работало под Graph API, поэтому я все еще ищу ответ на оригинальный вопрос.

2 ответа

Решение

В какой-то момент я установил BaseUrl для клиента https://graph.windows.net:443/{{tenantId}}Возможно, из-за различий в брендинге за последние несколько лет (Microsoft Graph против Azure Graph). Согласно действующим рекомендациям для Microsoft. Граф должен быть 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();

Согласно вашему описанию, вы хотите отправить электронное письмо, но получите ошибку 400.

Основываясь на моем тесте, мы можем использовать следующие шаги для отправки электронного письма.

шаг1, мы должны получить 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;

Мы можем сослаться на простой код в официальном документе.

шаг2, мы можем использовать следующий код для отправки почты:

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;
        }
Другие вопросы по тегам