Erste Schritte mit Microsoft Graph in einer universellen Windows 10-App

Sie erstellen Apps für Unternehmenskunden? Ihre App funktioniert möglicherweise nicht, wenn Ihr Unternehmenskunde Enterprise Mobility-Sicherheitsfunktionen wie bedingten Gerätezugriff aktiviert. In diesem Fall treten bei Ihren Kunden möglicherweise Fehler auf.

Zur Unterstützung aller Unternehmenskunden über alle Unternehmensszenarien hinweg müssen Sie den Azure AD-Endpunkt verwenden und Ihr Apps mithilfe des Azure-Verwaltungsportals verwalten. Weitere Informationen finden Sie unter Entscheiden zwischen dem Azure AD- und dem Azure AD v2.0-Endpunkt.

Dieser Artikel beschreibt die erforderlichen Aufgaben zum Abrufen eines Zugriffstokens vom Azure AD v2.0-Endpunkt und zum Aufrufen von Microsoft Graph. Er führt Sie schrittweise durch den Code im Microsoft Graph Connect-Beispiel für UWP (Bibliothek) und erläutert die wichtigsten Konzepte, die in eine App implementiert werden müssen, die Microsoft Graph verwendet.

Sie möchten keine App erstellen? Verwenden Sie für einen schnellen Einstieg den Microsoft Graph-Schnellstart, oder laden Sie das Microsoft Graph Connect-Beispiel für UWP (Bibliothek) herunter, auf dem dieser Artikel basiert. Beachten Sie auch, dass es eine REST-Version dieses Beispiels gibt.

Beispielbenutzeroberfläche

Das Beispiel enthält eine sehr einfache Benutzeroberfläche, die aus einer oberen Befehlsleiste, einer Schaltfläche zum Herstellen einer Verbindung, einer Schaltfläche zum Senden von E-Mails und aus einem Textfeld besteht, in das die E-Mail-Adresse des angemeldeten Benutzers automatisch eingetragen wird, die jedoch bearbeitet werden kann.

Die Schaltfläche zum Senden von E-Mails ist deaktiviert, wenn der Benutzer keine Verbindung hergestellt hat:

Bildschirm mit aktivierter Schaltfläche zum Verbinden und deaktivierter Schaltfläche zum Senden von E-Mails

Die obere Befehlsleiste enthält eine Schaltfläche zum Trennen der Verbindung, wenn der Benutzer eine Verbindung hergestellt hat:

Bildschirm mit der E-Mail-Adresse des verbundenen Benutzers und aktivierter Schaltfläche zum Senden von E-Mails

Die gesamten Zeichenfolgen der Beispielbenutzeroberfläche werden in der Datei „Resources.resw“ innerhalb des Objektordners gespeichert.

Voraussetzungen

Für die ersten Schritte benötigen Sie:

Registrieren der App

  1. Melden Sie sich beim App-Registrierungsportal entweder mit Ihrem persönlichen oder geschäftlichen Konto oder mit Ihrem Schulkonto an.
  2. Klicken Sie auf App hinzufügen.
  3. Geben Sie einen Namen für die App ein, und wählen Sie Anwendung erstellen aus.

    Die Registrierungsseite wird angezeigt, und die Eigenschaften der App werden aufgeführt.

  4. Wählen Sie unter Plattformen die Option Plattform hinzufügen aus.

  5. Wählen Sie Systemeigene Anwendung.
  6. Kopieren Sie die Werte für die Client-ID (App-ID) und den Umleitungs-URI in die Zwischenablage. Sie müssen diese Werte in die Beispiel-App eingeben.

    Die App-ID ist ein eindeutiger Bezeichner für Ihre App. Der Umleitungs-URI ist ein eindeutiger, von Windows 10 für jede Anwendung bereitgestellter URI, der sicherstellt, dass an diesen URI gesendete Nachrichten nur an diese Anwendung gesendet werden.

  7. Klicken Sie auf Speichern.

Konfigurieren des Projekts

  1. Öffnen Sie die Projektmappendatei für das Startprojekt in Visual Studio.
  2. Öffnen Sie die Datei App.xaml des Projekts, und suchen Sie den Knoten Application.Resources. Ersetzen Sie die Platzhalter der Anwendungs-ID und des Umleitungs-URI durch die entsprechenden Werte der App, die Sie registriert haben.
    <Application.Resources>
        <!-- Add your Client Id here. -->
        <x:String x:Key="ida:ClientID">ENTER_YOUR_CLIENT_ID</x:String>
        <!-- Add your Redirect URI here. -->
        <x:String x:Key="ida:ReturnUrl">ENTER_YOUR_REDIRECT_URI</x:String>
    </Application.Resources>

Installieren der Microsoft Authentication Library (MSAL)

Die Microsoft Authentifizierungsbibliothek enthält Klassen und Methoden, die das Authentifizieren von Benutzern über den Azure AD v2.0-Endpunkt vereinfachen.

  1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf den Projektnamen, und wählen Sie NuGet-Pakete verwalten... aus.
  2. Klicken Sie auf „Durchsuchen“, und suchen Sie nach Microsoft.Identity.Client.
  3. Wählen Sie die neueste Version der Microsoft Authentifizierungsbibliothek, und klicken Sie auf Installieren.

Installieren der Microsoft Graph-Clientbibliothek

  1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf den Projektnamen, und wählen Sie NuGet-Pakete verwalten... aus.
  2. Klicken Sie auf „Durchsuchen“, und suchen Sie nach Microsoft.Graph.
  3. Wählen Sie die neueste Version der Microsoft Graph Clientbibliothek, und klicken Sie auf Installieren.

Installieren der Newtonsoft.JSON-Bibliothek

  1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt XamarinConnect (Portable), und wählen Sie NuGet-Pakete verwalten... aus.
  2. Klicken Sie auf „Durchsuchen“, und suchen Sie nach „NewtonSoft.JSON“.
  3. Wählen Sie Version 9.0.1 der NewtonSoft.JSON-Bibliothek aus, und klicken Sie auf Installieren.

Erstellen der Klasse AuthenticationHelper.cs

Öffnen Sie die Datei AuthenticationHelper.cs im Startprojekt. Diese Datei wird den gesamten Authentifizierungscode sowie zusätzliche Logik enthalten, die Benutzerinformationen speichert und die Authentifizierung nur dann erzwingt, wenn der Benutzer die Verbindung zu der App getrennt hat. Diese Klasse enthält mindestens zwei Methoden: GetTokenForUserAsync und Signout. Wenn Sie die Microsoft Graph Clientbibliothek verwenden, müssen Sie eine dritte Methode hinzufügen: GetAuthenticatedClient.

Die Methode GetTokenHelperAsync wird ausgeführt, wenn der Benutzer sich authentifiziert, und anschließend jedes Mal, wenn die App Microsoft Graph aufruft.

Verwenden von Deklarationen

Clientbibliothek-Version

Stellen Sie sicher, dass die Datei die folgenden Deklarationen enthält:

using System;
using System.Diagnostics;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.Graph;
using Microsoft.Identity.Client;

Felder der Klasse

Stellen Sie sicher, dass Sie diese Felder in der Klasse AuthenticationHelper haben:

// The Client ID is used by the application to uniquely identify itself to the Azure AD v2.0 endpoint.
static string clientId = App.Current.Resources["ida:ClientID"].ToString();
public static string[] Scopes = { "User.Read", "Mail.Send" };
public static PublicClientApplication IdentityClientApp = new PublicClientApplication(clientId);
public static string TokenForUser = null;
public static DateTimeOffset Expiration;
private static GraphServiceClient graphClient = null;

In diesem Beispiel wird der GraphServicesClient in einem Feld gespeichert, sodass Sie ihn nur einmal erstellen müssen. Die MSAL-Klasse PublicClientApplication wird zum Authentifizieren des Benutzers verwendet. Im Feld Scopes sind die Microsoft Graph-Berechtigungsbereiche gespeichert, die die App anfordern muss, wenn der Benutzer sich authentifiziert.

GetTokenForUserAsync

Die Methode GetTokenForUserAsync verwendet die PublicClientApplicationClass und die ClientId-Einstellung, um ein Zugriffstoken für den Benutzer abzurufen. Wenn der Benutzer sich nicht bereits authentifiziert hat, wird die Authentifizierungsbenutzeroberfläche gestartet.

        public static async Task<string> GetTokenForUserAsync()
        {
            AuthenticationResult authResult;
            try
            {
                authResult = await IdentityClientApp.AcquireTokenSilentAsync(Scopes);
                TokenForUser = authResult.Token;
            }

            catch (Exception)
            {
                if (TokenForUser == null || Expiration <= DateTimeOffset.UtcNow.AddMinutes(5))
                {
                    authResult = await IdentityClientApp.AcquireTokenAsync(Scopes);

                    TokenForUser = authResult.Token;
                    Expiration = authResult.ExpiresOn;
                }
            }

            return TokenForUser;
        }

Signout

Die Methode Signout meldet alle Benutzer ab, die über die PublicClientApplication angemeldet sind (nur einen Benutzer, in diesem Fall), und hebt den Wert TokenForUser auf. Sie hebt auch den Wert GraphServicesClient auf.

Dies ist die Clientbibliothek-Version der Methode Signout.

        public static void SignOut()
        {
            foreach (var user in IdentityClientApp.Users)
            {
                user.SignOut();
            }
            graphClient = null;
            TokenForUser = null;

        }

GetAuthenticatedClient

Abschließend benötigen Sie eine Methode zum Erstellen einer GraphServicesClient. Diese Methode erstellt einen Client, der die Methode GetTokenForUserAsync bei jedem Aufruf von Microsoft Graph über den Client verwendet.

        public static GraphServiceClient GetAuthenticatedClient()
        {
            if (graphClient == null)
            {
                // Create Microsoft Graph client.
                try
                {
                    graphClient = new GraphServiceClient(
                        "https://graph.microsoft.com/v1.0",
                        new DelegateAuthenticationProvider(
                            async (requestMessage) =>
                            {
                                var token = await GetTokenForUserAsync();
                                requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
                            }));
                    return graphClient;
                }

                catch (Exception ex)
                {
                    Debug.WriteLine("Could not create a graph client: " + ex.Message);
                }
            }

            return graphClient;
        }

Senden einer E-Mail mit Microsoft Graph

Öffnen Sie die Datei MailHelper.cs in Ihrem Startprojekt. Diese Datei enthält den Code, durch den eine E-Mail erstellt und gesendet wird. Er besteht aus einer einzigen Methode – ComposeAndSendMailAsync –, die eine POST-Anforderung konstruiert und an den Endpunkt https://graph.microsoft.com/v1.0/me/microsoft.graph.SendMail sendet.

Die ComposeAndSendMailAsync-Methode verwendet die drei Zeichenfolgenwerte subject, bodyContent und recipients, die von der Datei „MainPage.xaml.cs“ an sie übergeben werden. Die Zeichenfolgen subject und bodyContent werden zusammen mit allen anderen Benutzeroberflächenzeichenfolgen in der Datei „Resources.resw“ gespeichert. Die Zeichenfolge recipients stammt aus dem Adressfeld in der App-Schnittstelle.

Stellen Sie sicher, dass die Datei „MailHelper.cs“ die folgenden using-Deklarationen enthält:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Graph;
using Windows.Storage;

Die erste Aufgabe innerhalb der Methode ComposeAndSendMailAsync besteht darin, das Foto des aktuellen Benutzers aus Microsoft Graph abzurufen. Diese Zeile ruft die Methode GetCurrentUserPhotoStreamAsync auf:

            // Get current user photo
            Stream photoStream = await GetCurrentUserPhotoStreamAsync();

Die vollständige Methode GetCurrentUserPhotoStreamAsync sieht wie folgt aus:

        // Gets the stream content of the signed-in user's photo. 
        // This snippet doesn't work with consumer accounts.
        public async Task<Stream> GetCurrentUserPhotoStreamAsync()
        {
            Stream currentUserPhotoStream = null;

            try
            {
                var graphClient = AuthenticationHelper.GetAuthenticatedClient();
                currentUserPhotoStream = await graphClient.Me.Photo.Content.Request().GetAsync();

            }

            // If the user account is MSA (not work or school), the service will throw an exception.
            catch (ServiceException)
            {
                return null;
            }

            return currentUserPhotoStream;

        }

Wenn der Benutzer kein Foto hat, wird mit dieser Logik eine andere Bilddatei abgerufen, die in das Projekt eingeschlossen wurde:

            // If the user doesn't have a photo, or if the user account is MSA, we use a default photo

            if (photoStream == null)
            {
                StorageFile file = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync("test.jpg");
                photoStream = (await file.OpenReadAsync()).AsStreamForRead();
            }

Nachdem nun ein Bilddatenstrom vorliegt, kann die Datei in OneDrive hochgeladen werden, indem die Methode UploadFileToOneDriveAsync aufgerufen wird:

            MemoryStream photoStreamMS = new MemoryStream();
            // Copy stream to MemoryStream object so that it can be converted to byte array.
            photoStream.CopyTo(photoStreamMS);

            DriveItem photoFile = await UploadFileToOneDriveAsync(photoStreamMS.ToArray());

Die vollständige Methode UploadFileToOneDriveAsync sieht wie folgt aus:

        // Uploads the specified file to the user's root OneDrive directory.
        public async Task<DriveItem> UploadFileToOneDriveAsync(byte[] file)
        {
            DriveItem uploadedFile = null;

            try
            {
                var graphClient = AuthenticationHelper.GetAuthenticatedClient();
                MemoryStream fileStream = new MemoryStream(file);
                uploadedFile = await graphClient.Me.Drive.Root.ItemWithPath("me.png").Content.Request().PutAsync<DriveItem>(fileStream);

            }


            catch (ServiceException)
            {
                return null;
            }

            return uploadedFile;
        }

Dieser Datenstrom kann auch zum Erstellen eines MessageAttachmentsCollectionPage-Objekts verwendet werden, das zusammen mit der Nachricht übergeben werden kann:

            MessageAttachmentsCollectionPage attachments = new MessageAttachmentsCollectionPage();
            attachments.Add(new FileAttachment
            {
                ODataType = "#microsoft.graph.fileAttachment",
                ContentBytes = photoStreamMS.ToArray(),
                ContentType = "image/png",
                Name = "me.png"
            });

Durch Aufruf der Methode GetSharingLinkAsync kann ein Freigabelink für die neu hochgeladene OneDrive-Datei abgerufen werden. Die Zeichenfolge bodyContent enthält einen Platzhalter für den Freigabelink:

            // Get the sharing link and insert it into the message body.
            Permission sharingLink = await GetSharingLinkAsync(photoFile.Id);
            string bodyContentWithSharingLink = String.Format(bodyContent, sharingLink.Link.WebUrl);

Die vollständige Methode GetSharingLinkAsync sieht wie folgt aus:

        public static async Task<Permission> GetSharingLinkAsync(string Id)
        {
            Permission permission = null;

            try
            {
                var graphClient = AuthenticationHelper.GetAuthenticatedClient();
                permission = await graphClient.Me.Drive.Items[Id].CreateLink("view").Request().PostAsync();
            }

            catch (ServiceException)
            {
                return null;
            }

            return permission;
        }

Da der Benutzer mehrere Adressen übergeben kann, besteht die nächste Aufgabe darin, die Zeichenfolge recipients in einen Satz von EmailAddress-Objekten zu unterteilen, die dann verwendet werden können, um die Liste der Recipients-Objekte zu erstellen, die im POST-Text der Anforderung übergeben werden.

            // Prepare the recipient list
            string[] splitter = { ";" };
            var splitRecipientsString = recipients.Split(splitter, StringSplitOptions.RemoveEmptyEntries);
            List<Recipient> recipientList = new List<Recipient>();

            foreach (string recipient in splitRecipientsString)
            {
                recipientList.Add(new Recipient { EmailAddress = new EmailAddress { Address = recipient.Trim() } });
            }

Die letzte Aufgabe besteht darin, ein Message-Objekt zu erstellen und dieses über den GraphServiceClient an den Endpunkt me/microsoft.graph.SendMail zu senden. Da die Zeichenfolge bodyContent ein HTML-Dokument ist, legt die Anforderung den Wert ContentType auf HTML fest.

            try
            {
                var graphClient = AuthenticationHelper.GetAuthenticatedClient();

                var email = new Message
                {
                    Body = new ItemBody
                    {
                        Content = bodyContentWithSharingLink,
                        ContentType = BodyType.Html,
                    },
                    Subject = subject,
                    ToRecipients = recipientList,
                    Attachments = attachments
                };

                try
                {
                    await graphClient.Me.SendMail(email, true).Request().PostAsync();
                }
                catch (ServiceException exception)
                {
                    throw new Exception("We could not send the message: " + exception.Error == null ? "No error message returned." : exception.Error.Message);
                }


            }

            catch (Exception e)
            {
                throw new Exception("We could not send the message: " + e.Message);
            }

Die vollständige Klasse sieht wie folgt aus:

    public class MailHelper
    {
        /// <summary>
        /// Compose and send a new email.
        /// </summary>
        /// <param name="subject">The subject line of the email.</param>
        /// <param name="bodyContent">The body of the email.</param>
        /// <param name="recipients">A semicolon-separated list of email addresses.</param>
        /// <returns></returns>
        public async Task ComposeAndSendMailAsync(string subject,
                                                            string bodyContent,
                                                            string recipients)
        {

            // Get current user photo
            Stream photoStream = await GetCurrentUserPhotoStreamAsync();


            // If the user doesn't have a photo, or if the user account is MSA, we use a default photo

            if (photoStream == null)
            {
                StorageFile file = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync("test.jpg");
                photoStream = (await file.OpenReadAsync()).AsStreamForRead();
            }

            MemoryStream photoStreamMS = new MemoryStream();
            // Copy stream to MemoryStream object so that it can be converted to byte array.
            photoStream.CopyTo(photoStreamMS);

            DriveItem photoFile = await UploadFileToOneDriveAsync(photoStreamMS.ToArray());

            MessageAttachmentsCollectionPage attachments = new MessageAttachmentsCollectionPage();
            attachments.Add(new FileAttachment
            {
                ODataType = "#microsoft.graph.fileAttachment",
                ContentBytes = photoStreamMS.ToArray(),
                ContentType = "image/png",
                Name = "me.png"
            });

            // Get the sharing link and insert it into the message body.
            Permission sharingLink = await GetSharingLinkAsync(photoFile.Id);
            string bodyContentWithSharingLink = String.Format(bodyContent, sharingLink.Link.WebUrl);

            // Prepare the recipient list
            string[] splitter = { ";" };
            var splitRecipientsString = recipients.Split(splitter, StringSplitOptions.RemoveEmptyEntries);
            List<Recipient> recipientList = new List<Recipient>();

            foreach (string recipient in splitRecipientsString)
            {
                recipientList.Add(new Recipient { EmailAddress = new EmailAddress { Address = recipient.Trim() } });
            }

            try
            {
                var graphClient = AuthenticationHelper.GetAuthenticatedClient();

                var email = new Message
                {
                    Body = new ItemBody
                    {
                        Content = bodyContentWithSharingLink,
                        ContentType = BodyType.Html,
                    },
                    Subject = subject,
                    ToRecipients = recipientList,
                    Attachments = attachments
                };

                try
                {
                    await graphClient.Me.SendMail(email, true).Request().PostAsync();
                }
                catch (ServiceException exception)
                {
                    throw new Exception("We could not send the message: " + exception.Error == null ? "No error message returned." : exception.Error.Message);
                }


            }

            catch (Exception e)
            {
                throw new Exception("We could not send the message: " + e.Message);
            }
        }


        // Gets the stream content of the signed-in user's photo. 
        // This snippet doesn't work with consumer accounts.
        public async Task<Stream> GetCurrentUserPhotoStreamAsync()
        {
            Stream currentUserPhotoStream = null;

            try
            {
                var graphClient = AuthenticationHelper.GetAuthenticatedClient();
                currentUserPhotoStream = await graphClient.Me.Photo.Content.Request().GetAsync();

            }

            // If the user account is MSA (not work or school), the service will throw an exception.
            catch (ServiceException)
            {
                return null;
            }

            return currentUserPhotoStream;

        }

        // Uploads the specified file to the user's root OneDrive directory.
        public async Task<DriveItem> UploadFileToOneDriveAsync(byte[] file)
        {
            DriveItem uploadedFile = null;

            try
            {
                var graphClient = AuthenticationHelper.GetAuthenticatedClient();
                MemoryStream fileStream = new MemoryStream(file);
                uploadedFile = await graphClient.Me.Drive.Root.ItemWithPath("me.png").Content.Request().PutAsync<DriveItem>(fileStream);

            }


            catch (ServiceException)
            {
                return null;
            }