Schreiben einer einseitigen JavaScript-App zum Abrufen von Outlook-Mail, -Kalender und -KontaktenWrite a JavaScript single-page app to get Outlook mail, calendar, and contacts

In diesem Leitfaden werden Sie schrittweise durch den Prozess des Erstellens einer einfachen einer einseitigen JavaScript-App zum Abrufen von Nachrichten in Office 365 oder Outlook.com geführt. Wenn Sie die hier beschriebenen Schritte ausführen, sollte der Quellcode in diesem Repository das Ergebnis sein.The purpose of this guide is to walk through the process of creating a simple single-page app that retrieves messages in Office 365 or Outlook.com. The source code in this repository is what you should end up with if you follow the steps outlined here.

In diesem Leitfaden wird Microsoft Graph(/graph/overview) zum Zugriff auf Outlook-Mail verwendet.This guide will use Microsoft Graph( to access Outlook mail. Microsoft empfiehlt die Verwendung von Microsoft Graph für den Zugriff auf Outlook-Mail, -Kalender und -Kontakte.Microsoft recommends using Microsoft Graph to access Outlook mail, calendar, and contacts. Verwenden Sie die Outlook-APIs nur dann direkt (über https://outlook.office.com/api), wenn Sie ein Feature benötigen, das in den Graph-Endpunkten nicht verfügbar ist.You should use the Outlook APIs directly (via https://outlook.office.com/api) only if you require a feature that is not available on the Graph endpoints. Eine Version dieses Beispiels mit Verwendung der Outlook-APIs finden Sie in dieser Verzweigung.For a version of this sample that uses the Outlook APIs, see this branch.

Hinweis

In diesem Leitfaden wird http-server verwendet, um einen einfachen Befehlszeilen-HTTP-Server für Entwicklungszwecke bereitzustellen. Das http-server-Paket erfordert Node.js und NPM für die Installation und Ausführung. Eine Verwendung von http-server ist jedoch nicht erforderlich, Sie können für dieses Lernprogramm einen beliebigen HTTP-Server verwenden.This guide uses http-server to provide a simple command-line HTTP server for development purposes. The http-server package requires Node.js and NPM to install and run. However, using http-server is not required, you can use any HTTP server for this tutorial.

Erstellen der AppCreate the app

Erstellen Sie ein leeres Verzeichnis, in dem Sie die App erstellen möchten. Für die Zwecke dieses Leitfadens wird davon ausgegangen, dass der Name des Verzeichnisses javascript-tutorial ist, Sie können aber gerne einen beliebigen Namen verwenden.Create an empty directory where you want to create your app. For the purposes of this guide I will assume the name of the directory is javascript-tutorial, but feel free to use any name you like.

Da unsere App clientseitig und JavaScript-basiert ist, benötigen wir einen HTTP-Server für unsere HTML- und JavaScript-Dateien. Für diesen Leitfaden können Sie gerne einen von Ihnen bevorzugten HTTP-Server verwenden. Zur Vereinfachung enthält dieses Handbuch Schritte zur Verwendung von http-server, um schnell einen Entwicklungswebserver in der Befehlszeile zu erstellen.Since our app will be client-side JavaScript-based, we need an HTTP server to serve our HTML and JavaScript files. Feel free to use your favorite HTTP server for this guide. To make things simple, this guide will include steps to use http-server to quickly create a development web server on the command line.

Einrichten des WebserversSetup the web server

Bevor Sie beginnen, müssen Sie Node.js installiert haben.Before you begin you'll need to have Node.js installed.

  1. Öffnen Sie eine Eingabeaufforderung, und legen Sie das aktuelle Verzeichnis auf das javascript-tutorial-Verzeichnis fest.Open a command prompt and set the current directory to the javascript-tutorial directory.

  2. Geben Sie den folgenden Befehl zur Installation von http-server ein:Enter the following command to install http-server:

    npm install http-server -g
    
  3. Geben Sie den folgenden Befehl ein, um den Server zu starten:Enter the following command to start the server:

    http-server
    
  4. Öffnen Sie einen Browser, und navigieren Sie zu http://localhost:8080. Wenn der Server ordnungsgemäß funktioniert, wird in etwa Folgendes angezeigt:Open a browser and browse to http://localhost:8080. If the server is working correctly, you should see something like the following:

    Browser, in dem eine Verzeichnisauflistung eines leeren Verzeichnisses angezeigt wird.

Dadurch wird bestätigt, dass der Server funktioniert. Nun können wir also mit dem Programmieren beginnen. Lassen Sie den Server ausgeführt. Wenn Sie ihn zu einem bestimmten Zeitpunkt beenden, starten Sie ihn unter Verwendung des http-server-Befehls im javascript-tutorial-Verzeichnis neu.This confirms that the server is working, so we're ready to start coding. You can leave the server running. If you do stop it at some point, be sure to restart it using the http-server command in the javascript-tutorial directory.

Entwerfen der AppDesigning the app

Unsere App ist sehr einfach. Wenn ein Benutzer die Website besucht, sieht er einen Link zum Anmelden und Anzeigen seiner E-Mails. Beim Klicken auf den Link gelangt er zur Azure-Anmeldeseite, wo er sich mit seinem Office 365- oder Outlook.com-Konto anmelden und unserer App Zugriff gewähren kann. Schließlich wird er zurück zu unserer App geleitet, die eine Liste der neuesten E-Mails im Posteingang des Benutzers anzeigt.Our app will be very simple. When a user visits the site, they will see a link to log in and view their email. Clicking that link will take them to the Azure login page where they can login with their Office 365 or Outlook.com account and grant access to our app. Finally, they will be redirected back to our app, which will display a list of the most recent email in the user's inbox.

Beginnen wir, indem wir eine HTML-Seite zu unserer App hinzufügen. Erstellen Sie mit Ihrem bevorzugten Editor eine Datei namens index.html im Verzeichnis javascript-tutorial, und fügen Sie den folgenden Code hinzu.Let's begin by adding an HTML page to our app. Using your favorite editor, create a file named index.html in the javascript-tutorial directory, and add the following code.

Inhalte der Datei index.htmlContents of the index.html file

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>Outlook SPA Demo</title>
  <link href="//ajax.aspnetcdn.com/ajax/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
  <link href="//ajax.aspnetcdn.com/ajax/bootstrap/3.3.6/css/bootstrap-theme.min.css" rel="stylesheet">
  <link href="style.css" rel="stylesheet" type="text/css" />

  <script src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-2.2.3.min.js"></script>
  <script src="//ajax.aspnetcdn.com/ajax/bootstrap/3.3.6/bootstrap.min.js"></script>
  <script src="//kjur.github.io/jsrsasign/jsrsasign-latest-all-min.js"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.min.js"></script>
  <script src="graph-js-sdk-web.js"></script>
  <script src="outlook-demo.js"></script>
</head>

<body>
  <nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container">
      <div class="navbar-header">
        <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
          <span class="sr-only">Toggle navigation</span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
        </button>
        <a class="navbar-brand" href="#">Outlook SPA Demo</a>
      </div>
      <div id="navbar" class="navbar-collapse collapse">
        <ul class="nav navbar-nav authed-nav">
          <li id='home-nav'><a href="#">Home</a></li>
          <li id='inbox-nav'><a href="#inbox">Inbox</a></li>
        </ul>
        <ul class="nav navbar-nav navbar-right authed-nav">
          <li><a href="#signout">Sign out</a></li>
        </ul>
      </div>
    </div>
  </nav>

  <div class="container main-container">
    <div id="signin-prompt" class="jumbotron page">
      <h1>Outlook SPA Demo</h1>
      <p>This example shows how to get an OAuth token from Azure using the <a href="https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-protocols-implicit/">implicit grant flow</a> and to use that token to make calls to the Outlook APIs.</p>
      <p>
        <a class="btn btn-lg btn-primary" href="#" role="button" id="connect-button">Connect to Outlook</a>
      </p>
    </div>
    <!-- logged in user welcome -->

    <!-- unsupported browser message -->

    <!-- error message -->

    <!-- inbox display -->

    <!-- token display -->

  </div>
</body>
</html>

Speichern Sie die Datei, und aktualisieren Sie Ihren Browser. Sie sollten jetzt die sehr einfache Startseite für die App sehen. Hier eine Übersicht der Funktionsweise des Codes:Save the file and refresh your browser. You should now see the very simple home page for the app. To recap what the code does:

Die Arbeitsweise der Seite basiert auf den Hashwerten in den HREFs auf den Schaltflächen in der Seite. Durch Klicken auf diese Schaltflächen wird keine neue Seite geladen. Stattdessen wird JavaScript-Code aufgerufen, um Daten zu laden und die Seite zu aktualisieren.The way the page will work is based on the hash values in the HREF's on the buttons on the page. Clicking this buttons will not load a new page, but will instead invoke JavaScript code to load data and update the page.

Erstellen Sie eine neue Datei mit dem Namen style.css im Verzeichnis javascript-tutorial. Fügen Sie den folgenden Code zu der Datei hinzu, und speichern Sie sie.Create a new file named style.css in the javascript-tutorial directory. Add the following code to the file and save it.

Inhalte der Datei style.cssContents of the style.css file

body {
  padding-top: 70px;
}

Erstellen Sie schließlich eine neue Datei mit dem Namen outlook-demo.js im Verzeichnis javascript-tutorial. Fügen Sie den folgenden Code zu der Datei hinzu, und speichern Sie sie.Finally create a new file named outlook-demo.js in the javascript-tutorial directory. Add the following code to the file and save it.

Inhalte der Datei outlook-demo.jsContents of the outlook-demo.js file

$(function() {

  // Check for browser support for sessionStorage
  if (typeof(Storage) === 'undefined') {
    render('#unsupportedbrowser');
    return;
  }

  // Check for browser support for crypto.getRandomValues
  var cryptObj = window.crypto || window.msCrypto; // For IE11
  if (cryptObj === undefined || cryptObj.getRandomValues === 'undefined') {
    render('#unsupportedbrowser');
    return;
  }

  render(window.location.hash);

  $(window).on('hashchange', function() {
    render(window.location.hash);
  });

  function render(hash) {

    var action = hash.split('=')[0];

    // Hide everything
    $('.main-container .page').hide();

    var isAuthenticated = false;

    var pagemap = {

      // Welcome page
      '': function() {
        renderWelcome(isAuthenticated);
      },

      // Receive access token

      // Signout

      // Error display

      // Display inbox

      // Shown if browser doesn't support session storage
      '#unsupportedbrowser': function () {
        $('#unsupported').show();
      }
    }

    if (pagemap[action]){
      pagemap[action]();
    } else {
      // Redirect to home page
      window.location.hash = '#';
    }
  }

  function setActiveNav(navId) {
    $('#navbar').find('li').removeClass('active');
    $(navId).addClass('active');
  }

  function renderWelcome(isAuthed) {
    if (isAuthed) {
      $('#username').text(sessionStorage.userDisplayName);
      $('#logged-in-welcome').show();
      setActiveNav('#home-nav');
    } else {
      $('#connect-button').attr('href', buildAuthUrl());
      $('#signin-prompt').show();
    }
  }

  // OAUTH FUNCTIONS =============================

  // OUTLOOK API FUNCTIONS =======================

  // HELPER FUNCTIONS ============================

});

Dieser Code erstellt eine render-Methode, die Teile der Seite basierend auf dem aktuellen #-Wert in der URL selektiv anzeigt. Für jeden unterstützten Hashwert gibt es einen entsprechenden Eintrag im pagemap-Objekt. Derzeit gibt es für die Startseite den leeren Wert und einen neuen Wert #unsupportedbrowser. Das allererste, was der Code macht, ist nach Unterstützung der Storage-Benutzeroberfläche und der crypto.getRandomValues-Funktion zu suchen. Die App verwendet die Webspeicher-API zum Speichern von Zugriffstoken und die crypto.getRandomValues-Funktion zum Generieren von Nonce-Werten, wir müssen daher sicherstellen, dass der Browser diese unterstützt (was bei den meisten modernen Browsern der Fall ist).This code creates a render method, which selectively shows parts of the page based on the current # value in the URL. For each supported hash value, there's a corresponding entry in the pagemap object. Right now there the blank value for the home page, and a new value #unsupportedbrowser. The very first thing the code does is check for support of the Storage interface and the crypto.getRandomValues function. The app is going to use the Web Storage API to store access tokens, and the crypto.getRandomValues function to generate nonce values, so we need to make sure the browser supports them (most modern browsers do).

Vorausgesetzt, Ihr Browser unterstützt Webspeicher und crypto.getRandomValues, wird durch Aktualisieren an diesem Punkt jetzt die Startseite wie zuvor angezeigt. Wenn Sie die URL im Browser in http://localhost:8080/#unsupportedbrowser ändern, sollte die Navigationsleiste oben angezeigt werden, der Rest der Seite ist aber leer. Wir fügen nun eine Notiz ein, um dem Benutzer mitzuteilen, was passiert ist.Assuming your browser supports Web Storage and crypto.getRandomValues, at this point refrehsing the page should show the home page as before. If you change the URL in the browser to http://localhost:8080/#unsupportedbrowser, you should see the nav bar at the top, but the rest of the page is blank. Let's add in a note to tell the user what happened.

Öffnen Sie index.html, und suchen Sie die folgende Zeile:Open index.html and locate the following line:

<!-- unsupported browser message -->

Fügen Sie unmittelbar nach dieser Zeile den folgenden Code hinzu:Add the following code immediately after that line.

Neuer Code in index.htmlNew code in index.html

<div id="unsupported" class="jumbotron page">
  <h1>Oops....</h1>
  <p>This page requires browser support for <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API">session storage</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/API/RandomSource/getRandomValues"><code>crypto.getRandomValues</code></a>. Unfortunately, your browser does not support one or both features. Please visit this page using a different browser.</p>
</div>

Speichern Sie die Änderung, und navigieren Sie zu http://localhost:8080/#unsupportedbrowser. Nun sollte die Meldung zum nicht unterstützten Browser angezeigt werden.Save the change and browse to http://localhost:8080/#unsupportedbrowser. You should now see the unsupported browser message.

Registrieren der AppRegister the app

Wichtig

Neue App-Registrierungen sollten im Anwendungsregistrierungsportal erstellt und verwaltet werden, damit sie mit Outlook.com kompatibel sind. Erstellen Sie neue App-Registrierungen nur dann im Azure-Verwaltungsportal Folgendes auf Ihre App zutrifft:New app registrations should be created and managed in the new Application Registration Portal to be compatible with Outlook.com. Only create new app registrations in the Azure Management Portal if your app:

  • Sie verwendet den OAuth2 Client Credentials Grant-Fluss oderUses the OAuth2 Client Credentials Grant Flow, or
  • Sie muss neben Outlook auf andere Office 365-Arbeitslasten zugreifen (z. B. OneDrive for Business oder SharePoint)Needs to access other Office 365 workloads besides Outlook (such as OneDrive for Business or SharePoint)

Beachten Sie, dass mithilfe des Azure-Verwaltungsportals registrierte Apps nicht mit Outlook.com kompatibel sind und dass Berechtigungsbereiche nicht dynamisch angefordert werden können.Bear in mind that apps registered using the Azure Management Portal will not be compatible with Outlook.com, and will not be able to dynamically request permissions scopes. Vorhandene App-Registrierungen, die im Azure-Verwaltungsportal erstellt wurden, funktionieren weiterhin nur für Office 365.Existing app registrations that were created in the Azure Management Portal will continue to work for Office 365 only. Diese Registrierungen werden nicht im Anwendungsregistrierungsportal angezeigt und müssen im Azure-Verwaltungsportal verwaltet werden.These registrations do not show up in the Application Registration Portal and must be managed in the Azure Management Portal.

KontoanforderungenAccount requirements

Um das Anwendungsregistrierungsportal zu verwenden, benötigen Sie entweder ein Office 365-Geschäfts- oder Schulkonto oder ein Microsoft-Konto. Wenn Sie nicht über eines dieser Konten verfügen, haben Sie verschiedene Möglichkeiten:In order to use the Application Registration Portal, you need either an Office 365 work or school account, or a Microsoft account. If you don't have either of these, you have a number of options:

  • Registrieren Sie sich hier für ein neues Microsoft-Konto.Sign up for a new Microsoft account here.
  • Sie haben mehrere Möglichkeiten, ein Office 365-Abonnement zu erhalten:You can obtain an Office 365 subscription in a couple of different ways:

REST-API-VerfügbarkeitREST API availability

Die REST-API ist derzeit auf allen Office 365-Konten, die über Exchange Online verfügen, sowie auf allen Outlook.com-Konten aktiviert.The REST API is currently enabled on all Office 365 accounts that have Exchange Online, and all Outlook.com accounts.

Wechseln Sie zum App-Registrierungsportal, um schnell eine App-ID zu erhalten.Head over to the Application Registration Portal to quickly get an application ID.

  1. Melden Sie sich über den Link Anmelden mit Ihrem Microsoft-Konto (Outlook.com) oder Ihrem Geschäfts-, Schul- oder Unikonto (Office 365) an.Using the Sign in link, sign in with either your Microsoft account (Outlook.com), or your work or school account (Office 365).
  2. Klicken Sie auf die Schaltfläche App hinzufügen. Geben Sie javascript-tutorial für den Namen ein, und klicken Sie auf Anwendung erstellen.Click the Add an app button. Enter javascript-tutorial for the name and click Create application.
  3. Suchen Sie den Abschnitt Plattformen, und klicken Sie auf Plattform hinzufügen. Wählen Sie Web aus, und geben Sie dann http://localhost:8080 unter Umleitungs-URIs ein. Stellen Sie sicher, dass die Option Impliziten Fluss zulassen aktiviert ist.Locate the Platforms section, and click Add Platform. Choose Web, then enter http://localhost:8080 under Redirect URIs. Make sure that there is a check next to ALlow Implicit Flow.
  4. Klicken Sie auf Speichern, um die Registrierung abzuschließen. Kopieren Sie die Anwendungs-ID, und speichern Sie sie. Wir benötigen sie bald.Click Save to complete the registration. Copy the Application Id and save it. We'll need it soon.

So sollten die Details Ihrer App-Registrierung aussehen, wenn Sie fertig sind.Here's what the details of your app registration should look like when you are done.

Screenshot der abgeschlossenen App-Registrierung im App-Registrierungsportal

Implementieren von OAuth2Implementing OAuth2

Unser Ziel in diesem Abschnitt besteht darin, den Fluss einer impliziten OAuth2-Genehmigung mit Azure AD über den Link auf unserer Homepage zu initiieren. Dieser Fluss wurde für SPA-Apps entworfen und ermöglicht es uns, ein Zugriffstoken abzurufen, ohne hierfür einen geheimen Clientschlüssel auszutauschen oder zusätzliche Anforderungen an einen Endpunkt vorzunehmen, der Token ausgibt.Our goal in this section is to make the link on our home page initiate the OAuth2 Implicit Grant flow with Azure AD. This flow is designed for SPA apps, and will allow us to get an access token without needing to exchange a client secret or do extra requests to a token issuing endpoint.

Der implizite Fluss ist ziemlich einfach. Der Benutzer navigiert zu einem von Azure gehosteten Authentifizierungslink, bei dem er sich anmeldet und Zugriff auf unsere App gewährt. In Azure erfolgt die Umleitung zu der App mit dem Ergebnis. Wenn sich der Benutzer erfolgreich angemeldet und seine Zustimmung abgegeben hat, enthält das Ergebnis ein Zugriffstoken und ein ID-Token.The implicit flow is pretty simple. The user browses to an authentication link hosted by Azure, where they sign in and grant access to our app. Azure redirects back to the app with the result. Assuming the user successfully signed in and granted consent, the result contains an access token and ID token.

Der erste Schritt besteht darin, den Authentifizierungslink zu generieren. Öffnen Sie die Datei outlook-demo.js, und fügen Sie die folgenden Variablen unmittelbar vor der Zeile // Check for browser support for sessionStorage hinzu:Our first step is to generate the authentication link. Open the outlook-demo.js file and add the following variables immediately before the // Check for browser support for sessionStorage line:

// App configuration
var authEndpoint = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize?';
var redirectUri = 'http://localhost:8080';
var appId = 'YOUR APP ID HERE';
var scopes = 'openid profile User.Read Mail.Read';

Ersetzen Sie den Wert YOUR APP ID HERE durch die Anwendungs-ID, die Sie als Teil des App-Registrierungsvorgangs generiert haben.Replace the YOUR APP ID HERE value with the application ID you generated as part of the app registration process.

Suchen Sie jetzt die Zeile // OAUTH FUNCTIONS in outlook-demo.js. Fügen Sie nach dieser Zeile die folgende Funktion hinzu.Now locate the // OAUTH FUNCTIONS line in outlook-demo.js. After this line, add the following function.

Die Funktion buildAuthUrlThe buildAuthUrl function

function buildAuthUrl() {
  // Generate random values for state and nonce
  sessionStorage.authState = guid();
  sessionStorage.authNonce = guid();

  var authParams = {
    response_type: 'id_token token',
    client_id: appId,
    redirect_uri: redirectUri,
    scope: scopes,
    state: sessionStorage.authState,
    nonce: sessionStorage.authNonce,
    response_mode: 'fragment'
  };

  return authEndpoint + $.param(authParams);
}

Suchen Sie die Zeile // HELPER FUNCTIONS, und fügen Sie danach die folgende Funktion hinzu.Locate the // HELPER FUNCTIONS line, and add the following function after it.

Die Funktion guidThe guid function

function guid() {
  var buf = new Uint16Array(8);
  cryptObj.getRandomValues(buf);
  function s4(num) {
    var ret = num.toString(16);
    while (ret.length < 4) {
      ret = '0' + ret;
    }
    return ret;
  }
  return s4(buf[0]) + s4(buf[1]) + '-' + s4(buf[2]) + '-' + s4(buf[3]) + '-' +
    s4(buf[4]) + '-' + s4(buf[5]) + s4(buf[6]) + s4(buf[7]);
}

Aktualisieren Sie jetzt die Funktion renderWelcome, um die Funktion buildAuthUrl aufzurufen, und legen Sie das href-Attribut der Anmeldeschaltfläche auf das Ergebnis fest. Ersetzen Sie die folgende Zeile:Now update the renderWelcome function to call the buildAuthUrl function and set the href attribute of the sign in button to the result. Replace this line:

$('#connect-button').attr('href', '#');

Durch:With:

$('#connect-button').attr('href', buildAuthUrl());

Speichern Sie Ihre Änderungen, und navigieren Sie zu http://localhost:8080. Wenn Sie den Mauszeiger über die Schaltfläche Mit Outlook verbinden bewegen, sollte diese folgendermaßen aussehen:Save your changes and browse to http://localhost:8080. If you hover over the Connect to Outlook button, it should look like:

https://login.microsoftonline.com/common/oauth2/v2.0/authorize?response_type=id_token+token&client_id=<SOME GUID>&redirect_uri=http%3A%2F%2Flocalhost%3A8080&scope=openid+profile+User.Read+Mail.Read&state=f175f48d-d277-9893-9c8d-dcc2a95ffe16&nonce=593a2b06-d77b-31c2-ae43-e74c0ebeb304&response_mode=fragment

Der Abschnitt <SOME GUID> sollte mit Ihrer Client-ID übereinstimmen. Klicken Sie auf den Link. Daraufhin wird die Anmeldeseite angezeigt. Melden Sie sich mit Ihrem Office 365- oder Outlook.com-Konto an. Ihr Browser sollte Sie ohne sichtbare Änderungen zurück zu unserer Startseite leiten.The <SOME GUID> portion should match your client ID. Click on the link and you should be presented with a sign in page. Sign in with your Office 365 or Outlook.com account. Your browser should redirect to back to our home page, with no visible change.

Der Grund, warum es keine Änderung gibt, liegt darin, dass wir keinen Code geschrieben haben, um die Umleitung zurück vom Azure-Authentifizierungsendpunkt zu verarbeiten. Das werden wir jetzt tun.The reason there is no change is because we haven't written any code to handle the redirect back from the Azure authentication endpoint. Let's do that now.

Abrufen des TokensGetting the token

Suchen Sie die render -Funktion in outlook-demo.js. Fügen Sie unmittelbar nach der Zeile // Receive access token den folgenden Code hinzu.Locate the render function in outlook-demo.js. Immediately after the // Receive access token line, add the following code.

Neuen Code zum Abrufen von ZugriffstokenNew code to receive access token

'#access_token': function() {
  handleTokenResponse(hash);
},

Fügen Sie als Nächstes den folgenden Code nach der Funktion buildAuthUrl hinzu.Next, add the following function after the buildAuthUrl function.

Die Funktion handleTokenResponseThe handleTokenResponse function

function handleTokenResponse(hash) {
  // clear tokens
  sessionStorage.removeItem('accessToken');
  sessionStorage.removeItem('idToken');

  var tokenresponse = parseHashParams(hash);

  // Check that state is what we sent in sign in request
  if (tokenresponse.state != sessionStorage.authState) {
    sessionStorage.removeItem('authState');
    sessionStorage.removeItem('authNonce');
    // Report error
    window.location.hash = '#error=Invalid+state&error_description=The+state+in+the+authorization+response+did+not+match+the+expected+value.+Please+try+signing+in+again.';
    return;
  }

  sessionStorage.authState = '';
  sessionStorage.accessToken = tokenresponse.access_token;

  // Get the number of seconds the token is valid for,
  // Subract 5 minutes (300 sec) to account for differences in clock settings
  // Convert to milliseconds
  var expiresin = (parseInt(tokenresponse.expires_in) - 300) * 1000;
  var now = new Date();
  var expireDate = new Date(now.getTime() + expiresin);
  sessionStorage.tokenExpires = expireDate.getTime();

  sessionStorage.idToken = tokenresponse.id_token;

  // Redirect to home page
  window.location.hash = '#';
}

Diese Funktion löscht alle zwischengespeicherten Token und analysiert dann die Tokenantwort. Sie überprüft, ob der Parameter state dem entspricht, was ursprünglich gesendet wurde, berechnet den Ablaufzeitpunkt für das Token und speichert dann die Token in der Sitzung. Bevor wir diesen Code testen, müssen wir Implementieren die Funktion parseHashParams implementieren. Fügen Sie diese Funktion nach der Funktion guid hinzu.This function clears any cached tokens, then parses the token response. It checks that the state parameter matches what we originally sent, calculates the expiration time for the token, then saves the tokens in the session. Before we test this code, we need to implement the parseHashParams function. Add this function after the guid function.

Die Funktion parseHashParamsThe parseHashParams function

function parseHashParams(hash) {
  var params = hash.slice(1).split('&');

  var paramarray = {};
  params.forEach(function(param) {
    param = param.split('=');
    paramarray[param[0]] = param[1];
  });

  return paramarray;
}

Nun werden wir die Funktion render so aktualisieren, dass geprüft wird, ob der Benutzer authentifiziert wurde. Ersetzen Sie die Zeile var isAuthenticated = false; durch den folgenden Code.Now let's update the render function to check if the user is authenticated. Replace the var isAuthenticated = false; line with the following code.

Der Code ist in der render-Funktion vorhanden.New code in the render function

// Check for presence of access token
var isAuthenticated = (sessionStorage.accessToken != null && sessionStorage.accessToken.length > 0);
renderNav(isAuthenticated);
renderTokens();

Wir werden nun eine Funktion zum Ändern der Navigationsleiste in Abhängigkeit davon hinzufügen, ob ein Benutzer authentifiziert wurde oder nicht. Fügen Sie die folgende Funktion nach der Funktion setActiveNav hinzu.Now, let's add a function to change the nav bar based on if a user is authenticated or not. Add the following function after the setActiveNav function.

Die Funktion renderNavThe renderNav function

function renderNav(isAuthed) {
  if (isAuthed) {
    $('.authed-nav').show();
  } else {
    $('.authed-nav').hide();
  }
}

Wir werden nun auch eine Funktion zum Anzeigen der Token hinzufügen, damit wir bestätigen können, das alles funktioniert. Fügen Sie die folgende Funktion nach der Funktion renderNav hinzu.Let's also add a function to display the tokens, so we can confirm that things are working. Add the following function after the renderNav function.

Die Funktion renderTokensThe renderTokens function

function renderTokens() {
  if (sessionStorage.accessToken) {
    // For demo purposes display the token and expiration
    var expireDate = new Date(parseInt(sessionStorage.tokenExpires));
    $('#token', window.parent.document).text(sessionStorage.accessToken);
    $('#expires-display', window.parent.document).text(expireDate.toLocaleDateString() + ' ' + expireDate.toLocaleTimeString());
    if (sessionStorage.idToken) {
      $('#id-token', window.parent.document).text(sessionStorage.idToken);
    }
    $('#token-display', window.parent.document).show();
  } else {
    $('#token-display', window.parent.document).hide();
  }
}

Wir müssen jetzt Code zu index.html hinzufügen, um unsere neue Funktion renderTokens zu unterstützen. Fügen Sie den folgenden Code nach der Zeile <!-- token display --> in index.html hinzu:Now we need to add code to index.html to support our new renderTokens function. Add the following code after the <!-- token display --> line in index.html.

Neuer Code in index.htmlNew code in index.html

<div id="token-display" class="page panel panel-default">
  <div class="panel-body">
    <h4>Access Token:</h4>
    <pre><code id="token"></code></pre>
    <h4>Expires:</h4>
    <p id="expires-display"></p>
    <h4>ID Token:</h4>
    <pre><code id="id-token"></code></pre>
  </div>
</div>

Speichern Sie Ihre Änderungen, und aktualisieren Sie Ihren Browser. Je nach den Status der Sitzung müssen Sie sich möglicherweise erneut anmelden. Nach der Anmeldung wird Folgendes angezeigt.Save your changes and refresh your browser. Depending on the state of your session you may have to sign in again. After signing in you should see something simliar to this.

Die App, die Token anzeigt

Wenn diese nicht angezeigt wird, wird vom Authentifizierungsprozess möglicherweise ein Fehler zurückgegeben. Wir fügen nun Code zu Behandlung einer Fehlerantwort hinzu. Fügen Sie als Erstes den folgenden Code nach der Zeile // Error display in der Funktion render hinzu:If you're not seeing that, there may be an error returned from the authentication process. Let's add some code to handle an error response. First, add the following after the // Error display line in the render function:

#error-Handler in der render-Funktion#error handler in render function

'#error': function () {
  var errorresponse = parseHashParams(hash);
  renderError(errorresponse.error, errorresponse.error_description);
},

Fügen Sie diese Funktion dann nach der Funktion renderToken hinzu.Then add this function after the renderToken function.

Die Funktion renderErrorThe renderError function

function renderError(error, description) {
  $('#error-name', window.parent.document).text('An error occurred: ' + decodePlusEscaped(error));
  $('#error-desc', window.parent.document).text(decodePlusEscaped(description));
  $('#error-display', window.parent.document).show();
}

Fügen Sie decodePlusEscaped unmittelbar nach der Funktion parseHashParams hinzu.Add the decodePlusEscaped just after the parseHashParams function.

Die Funktion decodePlusEscapedThe decodePlusEscaped function

function decodePlusEscaped(value) {
  // decodeURIComponent doesn't handle spaces escaped
  // as '+'
  if (value) {
    return decodeURIComponent(value.replace(/\+/g, ' '));
  } else {
    return '';
  }
}

Zum Schluss fügen Sie den HTML-Code zu index.html hinzu, um den Fehler anzuzeigen. Fügen Sie den folgenden Code nach der Zeile <!-- error message --> hinzu:Finally, let's add the HTML code into index.html to display the error. Add the following after the <!-- error message --> line.

Neuer Code in index.htmlNew code in index.html

<div id="error-display" class="page panel panel-danger">
  <div class="panel-heading">
    <h3 class="panel-title" id="error-name"></h3>
  </div>
  <div class="panel-body">
    <pre><code id="error-desc"></code></pre>
  </div>
</div>

Wenn Sie jetzt Ihren Browser aktualisieren und der Authentifizierungsprozess einen Fehler zurückgibt, wird dieser angezeigt.Now if you refresh your browser, if the authentication process returns an error, it will be displayed.

Tipp

Wenn Sie die Fehleranzeige testen möchten, können Sie einfach einen Fehler generieren, indem Sie den Anmeldevorgang mit einem neuen Benutzer starten (ein Benutzer, der sich zuvor noch nicht bei der App angemeldet hat). Wenn Sie aufgefordert werden, der App die Berechtigung zu erteilen, wählen Sie Nein oder Abbrechen aus. Auf diese Weise werden Sie zurück zur App geleitet, und es wird ein Fehler angezeigt, der besagt, dass der Benutzer keine Berechtigung erteilt hat.If you want to test the error display, an easy way to generate an error is to start the sign in process with a new user (one you have not signed into this app with before). When prompted to grant permission to the app, choose No or Cancel. This will redirect back to the app with an error indicating the user did not grant permission.

Bevor wir dies verbessern, implementieren wir nun die Möglichkeit des Abmeldens. Da alles in der Sitzung gespeichert wird, können wir die Sitzung einfach leeren.Before we improve on this, let's implement the ability to sign out. Since we keep everything in the session, we can just empty the session.

Fügen Sie als Erstes den folgenden Code nach der Zeile // Signout in der Funktion render hinzu:First add the following after the // Signout line in the render function:

#signout-Handler in der render-Funktion#signout handler in render function

'#signout': function () {
  clearUserState();

  // Redirect to home page
  window.location.hash = '#';
},

Fügen Sie als Nächstes die folgende Funktion nach der Funktion decodePlusEscaped hinzu.Then add the following function after the decodePlusEscaped function.

Die Funktion clearUserStateThe clearUserState function

function clearUserState() {
  // Clear session
  sessionStorage.clear();
}

Speichern Sie nun Ihre Änderungen, und aktualisieren Sie Ihren Browser. Sie sollten sich über die Schaltfläche Abmelden abmelden können.Now save your changes and refresh the browser. You should be able to use the Sign out button to sign out.

Verwenden des ID-TokensUsing the ID token

An diesem Punkt verfügen wir über ein Zugriffstoken, wir könnten also einfach fortfahren und die Outlook-API aufrufen. Zugriffstoken sind jedoch relativ kurzlebig. Sie laufen nach einer Stunde ab. Wir könnten den Benutzer zwar bitten, sich erneut anzumelden, dies ist aber keine gute Lösung. Im Idealfall sollten wir in der Lage sein, automatisch ein neues Token anzufordern. Die implizite Genehmigungsfluss verwendet nicht die Funktion zum Aktualisieren von Token, um ein neues Token zu erhalten, muss also eine neue Anforderung an den Autorisierungsendpunkt gesendet werden. Um dies automatisch auszuführen, müssen wir den Anmeldenamen des Benutzers einschließen, den wir aus dem ID-Token abrufen können, das in der anfänglichen Autorisierungsantwort enthalten ist. Wir können auch Daten wie den Anzeigenamen des Benutzers abrufen, den wir zum Personalisieren der App verwenden können.At this point we have an access token, so we could just proceed to calling the Outlook API. However, access tokens are relatively short-lived. After an hour they expire. While we could ask the user to sign in again, that's not a great solution. Ideally we would want to be able to silently request a new token. The implicit grant flow doesn't use refresh tokens, so the way to get a new token is to send another request to the authorization endpoint. To make it silent, we need to include the user's sign-in name, which we can get from the ID token that's included in our initial authorization response. We can also get data like the user's display name, which we can use to personalize the app.

Entsprechend der Azure-Dokumentation und der OpenID-Spezifikation MÜSSEN wir das Token vor der Verwendung überprüfen. Unsere Funktion führt daher eine grundlegende Validierung beim Analysieren des Tokens aus.According to the Azure documentation and the OpenID specification, we MUST validate the token before using it. So our function will do some basic validation as it parses the token.

Wichtig

In diesem Beispiel werden nicht alle erforderlichen Prüfungen ausgeführt, die in der OpenID-Spezifikation aufgeführt sind. Vor allem wird die Signatur im Token nicht überprüft. Für das Anfordern der Signierschlüssel aus Azure wäre derzeit eine serverseitige Komponente erforderlich, wir überspringen daher der Einfachheit halber diesen Schritt. In Produktions-Apps sollte dieser wichtige Schritt aber nicht übersprungen werden.This sample won't do all of the required validations listed in the OpenID spec. Most notably, it won't validate the signature on the token. Currently requesting the signing keys from Azure would require a server-side component, so we'll skip that step for the sake of simplicity. However, production apps should not skip this important step!

Fügen Sie die folgende Funktion in outlook-demo.js nach der Funktion handleTokenResponse hinzu.Add the following function in outlook-demo.js after the handleTokenResponse function.

Die Funktion validateIdTokenThe validateIdToken function

function validateIdToken(callback) {
  // Per Azure docs (and OpenID spec), we MUST validate
  // the ID token before using it. However, full validation
  // of the signature currently requires a server-side component
  // to fetch the public signing keys from Azure. This sample will
  // skip that part (technically violating the OpenID spec) and do
  // minimal validation

  if (null == sessionStorage.idToken || sessionStorage.idToken.length <= 0) {
    callback(false);
  }

  // JWT is in three parts seperated by '.'
  var tokenParts = sessionStorage.idToken.split('.');
  if (tokenParts.length != 3){
    callback(false);
  }

  // Parse the token parts
  var header = KJUR.jws.JWS.readSafeJSONString(b64utoutf8(tokenParts[0]));
  var payload = KJUR.jws.JWS.readSafeJSONString(b64utoutf8(tokenParts[1]));

  // Check the nonce
  if (payload.nonce != sessionStorage.authNonce) {
    sessionStorage.authNonce = '';
    callback(false);
  }

  sessionStorage.authNonce = '';

  // Check the audience
  if (payload.aud != appId) {
    callback(false);
  }

  // Check the issuer
  // Should be https://login.microsoftonline.com/{tenantid}/v2.0
  if (payload.iss !== 'https://login.microsoftonline.com/' + payload.tid + '/v2.0') {
    callback(false);
  }

  // Check the valid dates
  var now = new Date();
  // To allow for slight inconsistencies in system clocks, adjust by 5 minutes
  var notBefore = new Date((payload.nbf - 300) * 1000);
  var expires = new Date((payload.exp + 300) * 1000);
  if (now < notBefore || now > expires) {
    callback(false);
  }

  // Now that we've passed our checks, save the bits of data
  // we need from the token.

  sessionStorage.userDisplayName = payload.name;
  sessionStorage.userSigninName = payload.preferred_username;

  // Per the docs at:
  // https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-protocols-implicit/#send-the-sign-in-request
  // Check if this is a consumer account so we can set domain_hint properly
  sessionStorage.userDomainType =
    payload.tid === '9188040d-6c67-4c5b-b112-36a304b66dad' ? 'consumers' : 'organizations';

  callback(true);
}

Zusammenfassend verwendet die Funktion die jsrsasign-Bibliothek, um das Token zu analysieren, und überprüft dann Nonce, Zielgruppe, Aussteller und gültige Datumsangaben. Wenn alle Elemente die Überprüfung bestehen, werden drei Datenelemente aus dem Token extrahiert:To summarize, the function uses the jsrsasign library to parse the token, then validates the nonce, audience, issuer, and valid dates. If those all pass inspection, it then extracts three pieces of data from the token:

  • Der Anzeigename des Benutzers, den wir verwenden werden, um den Benutzer nach der Anmeldung zu begrüßen.The user's display name, which we'll use to welcome the user after signing in.
  • Der bevorzugte Benutzername des Benutzers, den wir für automatische Tokenanforderungen verwenden werden.The user's preferred user name, which we will use in silent token requests.
  • Wenn der Benutzer ein Konto eines Privatanwenders (Outlook.com) oder ein Geschäftskonto (Office 365) ist, das auch für automatische Tokenanforderungen verwendet wird.If the user is a consumer account (Outlook.com) or business account (Office 365), which is also used in silent token requests.

Jetzt werden wir die Funktion handleTokenResponse so ändern, dass diese Funktion aufgerufen wird. Suchen Sie die letzten beiden Zeilen der handleTokenResponse:Now let's modify the handleTokenResponse function to call this function. Locate the last two lines of the handleTokenResponse:

// Redirect to home page
window.location.hash = '#';

Ersetzen Sie diese Zeilen durch Folgendes:Replace those lines with the following

validateIdToken(function(isValid) {
  if (isValid) {
    // Re-render token to handle refresh
    renderTokens();

    // Redirect to home page
    window.location.hash = '#';
  } else {
    clearUserState();
    // Report error
    window.location.hash = '#error=Invalid+ID+token&error_description=ID+token+failed+validation,+please+try+signing+in+again.';
  }
});

Nun fügen Sie den HTML-Code zu index.html hinzu, um eine Willkommensseite anzuzeigen. Fügen Sie den folgenden Code nach der Zeile <!-- logged in user welcome --> hinzu:Now let's add the HTML code to index.html to show a welcome screen. Add the following code after the <!-- logged in user welcome --> line:

Neuer Code in index.htmlNew code in index.html

<div id="logged-in-welcome" class="jumbotron page">
  <h1>Outlook SPA Demo</h1>
  <p>Welcome <span id="username"></span>! Please use the nav menu to access your Outlook data.</p>
</div>

Speichern Sie Ihre Änderungen, und aktualisieren Sie Ihren Browser. Nach der Anmeldung sollte nun eine Willkommensnachricht mit dem Anzeigenamen des Benutzers angezeigt werden.Save your changes and refresh your browser. Now after logging in you should see a welcome message with the user's display name.

Aktualisieren des ZugriffstokensRefreshing the access token

Befassen wir uns nun mit einer weiteren OAuth-Aufgabe, bevor wir mit Outlook-Daten weitermachen: Das Aktualisieren von Token. Gemäß der Azure-Dokumentation wird hierfür eine ausgeblendete iframe-Anforderung verwendet. Im Wesentlichen wird ein iframe in die Seite eingefügt, und der Azure-Authentifizierungsendpunkt wird damit geladen. Wir fügen ein paar zusätzliche Parameter zu der URL hinzu, um die Anforderung automatisch auszuführen.Let's tackle one more OAuth task before moving on to Outlook data: refreshing tokens. Per the Azure documentation, the way to do this is with a hidden iframe request. Essentially we will insert an iframe into the page and have it load the Azure authentication endpoint. We'll add a few extra parameters to the URL to make it a silent request.

Fügen Sie die folgende Funktion zu outlook-demo.js nach der Funktion validateIdToken hinzu.Add the following function to outlook-demo.js after the validateIdToken function.

Die Funktion makeSilentTokenRequestThe makeSilentTokenRequest function

function makeSilentTokenRequest(callback) {
  // Build up a hidden iframe
  var iframe = $('<iframe/>');
  iframe.attr('id', 'auth-iframe');
  iframe.attr('name', 'auth-iframe');
  iframe.appendTo('body');
  iframe.hide();

  iframe.load(function() {
    callback(sessionStorage.accessToken);
  });

  iframe.attr('src', buildAuthUrl() + '&prompt=none&domain_hint=' +
    sessionStorage.userDomainType + '&login_hint=' +
    sessionStorage.userSigninName);
}

Beachten Sie, dass wir dieselbe buildAuthUrl-Funktion verwenden, um die Autorisierungs-URL abzurufen, wir fügen aber zusätzliche Abfrageparameter hinzu:Notice that we use the same buildAuthUrl function to get the authorization URL, but we add extra query parameters:

  • prompt=none: Wir fordern an, dass keine Anmeldeaufforderung angezeigt wird. Dies bewirkt, dass in Azure entweder die Token ohne Aufforderung zurückgegeben werden oder dass ein Fehler zurückgegeben wird, in dem erläutert wird, warum keine Token zurückgegeben werden können.prompt=none: We're requesting that no signin prompt be shown. This will cause Azure to either return the tokens with no prompting or return an error indicating why it cannot return tokens.
  • domain_hint=consumers oder domain_hint=organizations: Azure benötigt diese Informationen, um die automatische Anforderung zu verarbeiten. Diese teilen Azure im Wesentlichen mit, ob es sich bei dem Konto um ein Outlook.com-Konto oder um ein Office 365-Konto handelt.domain_hint=consumers or domain_hint=organizations: Azure needs this information to process the silent request. It basically tells Azure if the account is an Outlook.com account or an Office 365 account.
  • login_hint=<username>: Der Anmeldename des Benutzers. Azure benötigt diesen, um die automatische Anforderung zu verarbeiten.login_hint=<username>: The user's signin name. Azure needs this to process the silent request.

Nun benötigen wir einen Ort, wo wir diese Funktion verwenden können. Fügen Sie die folgende Funktion nach der Funktion makeSilentTokenRequest hinzu.Now we need a place to use this function. Add the following function after the makeSilentTokenRequest function.

Die Funktion getAccessTokenThe getAccessToken function

// Helper method to validate token and refresh
// if needed
function getAccessToken(callback) {
  var now = new Date().getTime();
  var isExpired = now > parseInt(sessionStorage.tokenExpires);
  // Do we have a token already?
  if (sessionStorage.accessToken && !isExpired) {
    // Just return what we have
    if (callback) {
      callback(sessionStorage.accessToken);
    }
  } else {
    // Attempt to do a hidden iframe request
    makeSilentTokenRequest(callback);
  }
}

Wir verwenden diese Methode immer dann, wenn wir die Zugriffstoken verwenden müssen. Die Funktion überprüft, ob das Token bald abläuft. Falls nicht, wird das Token nur aus der Sitzung zurückgegeben. Falls schon, wird das Token aktualisiert.We'll use this method anytime we need to use the access token. The function checks if the token is about to expire. If it isn't, then it just returns the token from the session. If it is, then it refreshes the token.

Wenn eine automatische Tokenanforderung vorgenommen wird und ein Problem mit dem Cookie des Benutzers vorliegt, kann der Azure-Dienst einen login_required- oder interaction_required-Fehler zurückgeben, in dem darauf hingewiesen wird, dass sich der Benutzer erneut anmelden muss. Wir fügen nun Code zu unserem #error-Handler hinzu, um diese Fehler abzufangen und den Browser zu einer interaktiven Anmeldung umzuleiten. Ersetzen Sie den vorhandenen #error-Handler in der Funktion render durch Folgendes:When making a silent token request, if there's something wrong with the user's cookie, the Azure service may return a login_required or interaction_required error, indicating that the user needs to sign in again. Let's add some code to our #error handler to catch those errors and redirect the browser to an interactive login. Replace the existing #error handler in the render function with the following.

Neuer #error-Handler in der Funktion renderNew #error handler in the render function

'#error': function () {
  var errorresponse = parseHashParams(hash);
  if (errorresponse.error === 'login_required' ||
      errorresponse.error === 'interaction_required') {
    // For these errors redirect the browser to the login
    // page.
    window.location = buildAuthUrl();
  } else {
    renderError(errorresponse.error, errorresponse.error_description);
  }
},

Zum Schluss fügen wir Code hinzu, um den ausgeblendeten iframe zu entfernen, sobald dessen Funktion abgeschlossen ist. Fügen Sie die folgenden Zeilen im oberen Bereich der Funktion handleTokenResponse hinzu:Finally, let's add code to remove the hidden iframe once it's done its work. Add the following lines to the top of the handleTokenResponse function:

// If this was a silent request remove the iframe
$('#auth-iframe').remove();

Verwenden der Mail-APIUsing the Mail API

Da wir nun ein Zugriffstoken abrufen können, bietet es sich an, mit der Mail-API fortzufahren.Now that we can get an access token, we're in a good position to do something with the Mail API. Beginnen wir damit, die JavaScript-Clientbibliothek von Microsoft Graph herunterzuladen.Let's start by downloading the Microsoft Graph JavaScript Client Library. Wir haben bereits einen Verweis darauf in der HTML hinzugefügt, wir müssen sie also nur herunterladen.We already added a reference to it in the HTML, so all we need to do is download it. Wechseln Sie zu https://github.com/microsoftgraph/msgraph-sdk-javascript, und laden Sie die Quelle herunter.Go to https://github.com/microsoftgraph/msgraph-sdk-javascript and download the source. Kopieren Sie ./lib/graph-js-sdk-web.js in das Verzeichnis javascript-tutorial.Copy the ./lib/graph-js-sdk-web.js into the javascript-tutorial directory.

Fügen Sie die folgende Funktion nach der Zeile // OUTLOOK API FUNCTIONS hinzu.Add the following function after the // OUTLOOK API FUNCTIONS line.

Die Funktion getUserInboxMessagesThe getUserInboxMessages function

function getUserInboxMessages(callback) {
  getAccessToken(function(accessToken) {
    if (accessToken) {
      // Create a Graph client
      var client = MicrosoftGraph.Client.init({
        authProvider: (done) => {
          // Just return the token
          done(null, accessToken);
        }
      });

      // Get the 10 newest messages
      client
        .api('/me/mailfolders/inbox/messages')
        .top(10)
        .select('subject,from,receivedDateTime,bodyPreview')
        .orderby('receivedDateTime DESC')
        .get((err, res) => {
          if (err) {
            callback(null, err);
          } else {
            callback(res.value);
          }
        });
    } else {
      var error = { responseText: 'Could not retrieve access token' };
      callback(null, error);
    }
  });
}

Diese verwendet den Graph-Client, um die ersten 10 Nachrichten im Posteingang abzurufen.This uses the Graph client to get the first 10 messages in the inbox. Die Funktion verwendet Abfrageparameter, um die zurückgegebenen Ergebnisse zu steuern.It uses query parameters to control the results we get back.

  • Mithilfe der top-Methode werden die Ergebnisse auf die ersten 10 Nachrichten beschränkt.Using the top method limits the results to the first 10 messages.
  • Mithilfe der select-Methode wird gesteuert, welche Felder für jede Nachricht zurückgegeben werden. In diesem Fall fordern wir nur die Felder subject, from, receivedDateTime und bodyPreview an.Using the select method controls which fields are returned for each message. In this case we only request the subject, from, receivedDateTime, and bodyPreview fields.
  • Mithilfe der orderby-Methode werden die Ergebnisse nach dem Feld receivedDateTime in absteigender Reihenfolge (neueste zuerst) sortiert.Using the orderby method sorts the results by the receivedDateTime field in descending order (newest first).

Fügen den folgenden Code nach der Zeile // Display inbox in der Funktion render hinzu.Add the following code after the // Display inbox line in the render function.

#inbox-Handler in der Funktion render#inbox handler in the render function

'#inbox': function () {
  if (isAuthenticated) {
    renderInbox();
  } else {
    // Redirect to home page
    window.location.hash = '#';
  }
},

Fügen Sie nun die folgende Funktion nach der Funktion renderWelcome hinzu.Now add the following function after the renderWelcome function.

Die Funktion renderInboxThe renderInbox function

function renderInbox() {
  setActiveNav('#inbox-nav');
  $('#inbox-status').text('Loading...');
  $('#message-list').empty();
  $('#inbox').show();

  getUserInboxMessages(function(messages, error){
    $('#inbox-status').text(JSON.stringify(messages));
  });
}

Fügen Sie den folgenden Code zu index.html nach der Zeile <!-- inbox display --> hinzu:Add the following code to index.html after the <!-- inbox display --> line:

Neuer Code in index.htmlNew code in index.html

<div id="inbox" class="page panel panel-default">
  <div class="panel-heading">
    <h1 class="panel-title">Inbox</h1>
  </div>
  <div id="inbox-status" class="panel-body">
  </div>
  <div class="list-group" id="message-list">
  </div>
</div>

Speichern Sie Ihre Änderungen, und aktualisieren Sie Ihren Browser. Beim Klicken auf die Schaltfläche Posteingang wird schließlich eine unformatierte Sicherungskopie von JSON-Nachrichten angezeigt. Diese ist nicht schön, bestätigt aber, dass unser API-Aufruf funktioniert. Wir korrigieren die App nun so, dass die Ergebnisse etwas ansprechender angezeigt werden.Save your changes and refresh your browser. When you click the Inbox button you should eventually see an unformatted JSON dump of messages. It isn't pretty, but it verifies that our API call is working. Let's fix the app to display the results in a nicer way.

Anzeigen der ErgebnisseDisplaying the results

Wir fügen nun eine Handlebar-Vorlage zu unserer Seite hinzu, um die Nachrichten anzuzeigen. Fügen Sie den folgenden Code zwischen dem letzten </div>- und </body>-Tags hinzu:Let's add a Handlebars template to our page to display the messages. Add the following code between the very last </div> and the </body> tags:

<!-- Handlebars template for message list -->
<script id="msg-list-template" type="text/x-handlebars-template">
  {{#each messages}}
  <div class="list-group-item">
    <h3 id="msg-from" class="list-group-item-">{{this.from.emailAddress.name}}</h3>
    <h4 id="msg-subject" class="list-group-item-heading">{{this.subject}}</h4>
    <p id="msg-received" class="list-group-item-heading text-muted"><em>Received: {{formatDate this.receivedDateTime}}</em></p>
    <p id="msg-preview" class="list-group-item-text text-muted"><em>{{this.bodyPreview}}</em></p>
  </div>
  {{/each}}
</script>

Um Datumsangaben ansprechend zu gestalten, registrieren wir ein Handlebar-Hilfsprogramm zum Formatieren von Datumsangaben. Fügen Sie die folgende Funktion nach der Funktion clearUserState in outlook-demo.js hinzu.To make date values look nice, we'll register a Handlebars helper to format dates. Add the following function after the clearUserState function in outlook-demo.js.

Das formatDate-HilfsprogrammThe formatDate helper

Handlebars.registerHelper("formatDate", function(datetime){
  // Dates from API look like:
  // 2016-06-27T14:06:13Z

  var date = new Date(datetime);
  return date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
});

Ersetzen Sie schließlich die $('#inbox-status').text(JSON.stringify(messages));-Funktion in der renderInbox-Funktion durch den folgenden Code.Finally, replace the line $('#inbox-status').text(JSON.stringify(messages)); in the renderInbox function with the following code.

if (error) {
  renderError('getUserInboxMessages failed', error);
} else {
  $('#inbox-status').text('Here are the 10 most recent messages in your inbox.');
  var templateSource = $('#msg-list-template').html();
  var template = Handlebars.compile(templateSource);

  var msgList = template({messages: messages});
  $('#message-list').append(msgList);
}

Speichern Sie Ihre Änderungen, und aktualisieren Sie Ihren Browser.Save your changes and refresh your browser.

Eine formatierte Liste von Nachrichten.

Hinzufügen von Kalender- und Kontakt-APIsAdding Calendar and Contacts APIs

Da Sie nun das Aufrufen der Outlook-Mail-API gemeistert haben, dürfte es kein Problem mehr sein, das gleiche für Kalender- und Kontakte-APIs zu tun.Now that you've mastered calling the Outlook Mail API, doing the same for Calendar and Contacts APIs is similar and easy.

Tipp

Wenn Sie die Schritte in diesem Lernprogramm befolgt haben, haben Sie wahrscheinlich ein Zugriffstoken in Ihrem Sitzungscookie gespeichert. Dieses Token ist nur für den Mail.Read-Bereich gültig. Um die Kalender- oder Kontakte-API aufzurufen, müssen wir neue Bereiche hinzufügen. Melden Sie sich unbedingt von der App ab, um das Sitzungscookie zu entfernen, damit Sie den Anmeldevorgang von vorne beginnen können, um ein neues Zugriffstoken zu erhalten.If you've followed along with the tutorial, you probably have an access token saved in your session cookie. That token will only be valid for the Mail.Read scope. In order to call the Calendar or Contacts API, we will need to add new scopes. Be sure sign out of the app to get rid of the session cookie so that you can start the login process from the beginning to get a new access token.

Für die Kalender-API:For Calendar API:

  1. Aktualisieren Sie die Variable scopes in outlook-demo.js, um den Calendars.Read-Bereich einzuschließen.Update the scopes variable in outlook-demo.js to include the Calendars.Read scope.

    var scopes = 'openid profile User.Read Mail.Read Calendars.Read';
    
  2. Fügen Sie einen #calendar-Handler zu der Funktion render hinzu.Add a #calendar handler in the render function.

    // Display calendar
    '#calendar': function () {
      if (isAuthenticated) {
        renderCalendar();
      } else {
        // Redirect to home page
        window.location.hash = '#';
      }
    },
    
  3. Fügen Sie eine renderCalendar-Funktion zu outlook-demo.js hinzu.Add a renderCalendar function in outlook-demo.js.

    function renderCalendar() {
      setActiveNav('#calendar-nav');
      $('#calendar-status').text('Loading...');
      $('#event-list').empty();
      $('#calendar').show();
    
      getUserEvents(function(events, error){
        if (error) {
          renderError('getUserEvents failed', error);
        } else {
          $('#calendar-status').text('Here are the 10 most recently created events on your calendar.');
          var templateSource = $('#event-list-template').html();
          var template = Handlebars.compile(templateSource);
    
          var eventList = template({events: events});
          $('#event-list').append(eventList);
        }
      });
    }
    
  4. Fügen Sie eine getUserEvents-Funktion zu outlook-demo.js hinzu.Add a getUserEvents function in outlook-demo.js.

    function getUserEvents(callback) {
      getAccessToken(function(accessToken) {
        if (accessToken) {
          // Create a Graph client
          var client = MicrosoftGraph.Client.init({
            authProvider: (done) => {
              // Just return the token
              done(null, accessToken);
            }
          });
    
          // Get the 10 newest events
          client
            .api('/me/events')
            .top(10)
            .select('subject,start,end,createdDateTime')
            .orderby('createdDateTime DESC')
            .get((err, res) => {
              if (err) {
                callback(null, err);
              } else {
                callback(res.value);
              }
            });
        } else {
          var error = { responseText: 'Could not retrieve access token' };
          callback(null, error);
        }
      });
    }
    
  5. Fügen Sie eine Kalender-Schaltfläche zur Navigationsleiste in index.html hinzu.Add a Calendar button to the nav bar in index.html.

    <li id='calendar-nav'><a href="#calendar">Calendar</a></li>
    
  6. Fügen Sie eine Kalenderanzeige zu index.html hinzu.Add a calendar display to index.html.

    <!-- calendar display -->
    <div id="calendar" class="page panel panel-default">
      <div class="panel-heading">
        <h1 class="panel-title">Calendar</h1>
      </div>
      <div id="calendar-status" class="panel-body">
      </div>
      <div class="list-group" id="event-list">
      </div>
    </div>
    
  7. Fügen Sie eine Handlebars-Vorlage für die Ereignisliste zu index.html hinzu.Add a Handlebars template for the event list to index.html.

    <!-- Handlebars template for event list -->
    <script id="event-list-template" type="text/x-handlebars-template">
      {{#each events}}
      <div class="list-group-item">
        <h4 id="event-subject" class="list-group-item-heading">{{this.subject}}</h4>
        <p id="event-start" class="list-group-item-heading">Start: {{formatDate this.start.dateTime}}</p>
        <p id="event-end" class="list-group-item-heading">End: {{formatDate this.end.dateTime}}</p>
      </div>
      {{/each}}
    </script>
    
  8. Aktualisieren Sie den Browser. Nach der Anmeldung klicken Sie auf die Schaltfläche Kalender in der Navigationsleiste.Refresh the browser. After signing in click the Calendar button in the nav bar.

Für die Kontakte-API:For Contacts API:

  1. Aktualisieren Sie die Variable scopes in outlook-demo.js, um den Contacts.Read-Bereich einzuschließen.Update the scopes variable in outlook-demo.js to include the Contacts.Read scope.

    var scopes = 'openid profile User.Read Mail.Read Contacts.Read';
    
  2. Fügen Sie einen #contacts-Handler zu der Funktion render hinzu.Add a #contacts handler in the render function.

    // Display contacts
    '#contacts': function () {
      if (isAuthenticated) {
        renderContacts();
      } else {
        // Redirect to home page
        window.location.hash = '#';
      }
    },
    
  3. Fügen Sie eine renderContacts-Funktion zu outlook-demo.js hinzu.Add a renderContacts function in outlook-demo.js.

    function renderContacts() {
      setActiveNav('#contacts-nav');
      $('#contacts-status').text('Loading...');
      $('#contact-list').empty();
      $('#contacts').show();
    
      getUserContacts(function(contacts, error){
        if (error) {
          renderError('getUserContacts failed', error);
        } else {
          $('#contacts-status').text('Here are your first 10 contacts.');
          var templateSource = $('#contact-list-template').html();
          var template = Handlebars.compile(templateSource);
    
          var contactList = template({contacts: contacts});
          $('#contact-list').append(contactList);
        }
      });
    }
    
  4. Fügen Sie eine getUserContacts-Funktion zu outlook-demo.js hinzu.Add a getUserContacts function in outlook-demo.js.

    function getUserContacts(callback) {
      getAccessToken(function(accessToken) {
        if (accessToken) {
          // Create a Graph client
          var client = MicrosoftGraph.Client.init({
            authProvider: (done) => {
              // Just return the token
              done(null, accessToken);
            }
          });
    
          // Get the first 10 contacts in alphabetical order
          // by given name
          client
            .api('/me/contacts')
            .top(10)
            .select('givenName,surname,emailAddresses')
            .orderby('givenName ASC')
            .get((err, res) => {
              if (err) {
                callback(null, err);
              } else {
                callback(res.value);
              }
            });
        } else {
          var error = { responseText: 'Could not retrieve access token' };
          callback(null, error);
        }
      });
    }
    
  5. Fügen Sie eine Kontakte-Schaltfläche zur Navigationsleiste in index.html hinzu.Add a Contacts button to the nav bar in index.html.

    <li id='contacts-nav'><a href="#contacts">Contacts</a></li>
    
  6. Fügen Sie eine Kontaktanzeige zu index.html hinzu.Add a contacts display to index.html.

    <!-- contacts display -->
    <div id="contacts" class="page panel panel-default">
      <div class="panel-heading">
        <h1 class="panel-title">Contacts</h1>
      </div>
      <div id="contacts-status" class="panel-body">
      </div>
      <div class="list-group" id="contact-list">
      </div>
    </div>
    
  7. Fügen Sie eine Handlebars-Vorlage für die Kontakteliste zu index.html hinzu.Add a Handlebars template for the contact list to index.html.

    <!-- Handlebars template for contact list -->
    <script id="contact-list-template" type="text/x-handlebars-template">
      {{#each contacts}}
      <div class="list-group-item">
        <h4 id="contact-name" class="list-group-item-heading">{{this.givenName}} {{this.surname}}</h4>
        <p id="contact-email" class="list-group-item-heading">Email: {{this.emailAddresses.0.address}}</p>
      </div>
      {{/each}}
    </script>
    
  8. Aktualisieren Sie den Browser. Nach der Anmeldung klicken Sie auf die Schaltfläche Kontakte in der Navigationsleiste.Refresh the browser. After signing in click the Contacts button in the nav bar.