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

In diesem Leitfaden werden Sie schrittweise durch den Prozess des Erstellens einer Python-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 Python web 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 zum Zugriff auf Outlook-Mail verwendet. Microsoft empfiehlt die Verwendung von Microsoft Graph für den Zugriff auf Outlook-Mail, -Kalender und -Kontakte. 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. Eine Version dieses Beispiels mit Verwendung der Outlook-APIs finden Sie in dieser Verzweigung.This guide will use Microsoft Graph to access Outlook mail. Microsoft recommends using Microsoft Graph to access Outlook mail, calendar, and contacts. 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. For a version of this sample that uses the Outlook APIs, see this branch.

In diesem Leitfaden wird davon ausgegangen, dass Sie Python und Django bereits installiert haben und auf Ihrem Entwicklungscomputer ausführen. Dieses Beispiel wurde mithilfe von Python Version 3.6.3 und Django 2.0.2 erstellt.This guide assumes that you already have Python and Django installed and working on your development machine. This sample was created using Python version 3.6.3 and Django 2.0.2.

Erstellen der AppCreate the app

Lassen Sie uns direkt loslegen. Öffnen Sie ein Eingabeaufforderungsfenster, und legen Sie das aktuelle Verzeichnis auf einen Ort fest, an dem Sie Ihren Code speichern möchten. Führen Sie den folgenden Befehl aus, um ein neues Django-Projekt zu erstellen.Let's dive right in! Open a command prompt and set the current directory to a location where you want to store your code. Run the following command to create a new Django project.

django-admin.py startproject python_tutorial

Auf diese Weise wird ein neues Unterverzeichnis mit dem Namen python_tutorial erstellt. Ändern Sie das aktuelle Verzeichnis des Eingabeaufforderungsfensters in dieses neue Unterverzeichnis, und führen Sie den folgenden Befehl aus, um zu überprüfen, ob alles funktioniert.This creates a new subdirectory called python_tutorial. Change your command prompt's current directory to this new subdirectory and run the following command to verify that everything is working.

python manage.py runserver

Öffnen Sie einen Webbrowser, sobald der Server gestartet wird, und navigieren Sie zu http://localhost:8000/. Es sollte eine Meldung angezeigt werden, dass der Vorgang erfolgreich war.Once the server starts, open a web browser and browse to http://localhost:8000/. You should see a success message.

Die standardmäßige Django-Willkommensseite.

Wir wollen nun eine App zu unserem neuen Projekt hinzufügen, wie auch in der Meldung vorgeschlagen wird. Führen Sie an der Eingabeaufforderung den folgenden Befehl aus:As the success message suggests, let's add an app to our new project. On the command prompt, run the following command.

python manage.py startapp tutorial

Auf diese Weise wird ein neues Unterverzeichnis unter dem python_tutorial-Verzeichnis sowie eine Reihe von Dateien erstellt.This creates a new subdirectory under the python_tutorial directory and a number of files.

Öffnen Sie die Datei .\python_tutorial\settings.py, und fügen Sie die neue App tutorial zu der Einstellung INSTALLED_APPS hinzu.Open the .\python_tutorial\settings.py file and add the new tutorial app to the INSTALLED_APPS setting.

Neuer Wert von INSTALLED_APPS in .\python_tutorial\settings.pyNew value of INSTALLED_APPS in .\python_tutorial\settings.py

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'tutorial',
)

Speichern Sie Ihre Änderungen. Führen Sie über die Befehlszeile den folgenden Befehl aus, um die Datenbank für die App zu initialisieren.Save your changes. From the command line, run the following command to initialize the database for the app.

python manage.py migrate

Fügen wir nun etwas Code zur App hinzu, um sicherzustellen, dass sie funktioniert. Öffnen Sie die Datei .\tutorial\views.py, und fügen Sie den folgenden Code hinzu.Let's add a bit of code to the app just to make sure that it is working. Open the .\tutorial\views.py file and add the following code.

home-Funktion in der Datei .\tutorial\views.pyhome function in the .\tutorial\views.py file

from django.http import HttpResponse, HttpResponseRedirect

def home(request):
  return HttpResponse("Welcome to the tutorial.")

Erstellen Sie eine neue Datei im tutorial-Verzeichnis mit dem Namen urls.py. Fügen Sie dieser Datei den folgenden Code hinzu.Create a new file in the tutorial directory called urls.py. Add the following code to this file.

Inhalt der Datei .\tutorial\urls.pyContents of the .\tutorial\urls.py file

from django.conf.urls import url 
from tutorial import views 

app_name = 'tutorial'
urlpatterns = [ 
  # The home view ('/tutorial/') 
  url(r'^$', views.home, name='home'), 
  # Explicit home ('/tutorial/home/') 
  url(r'^home/$', views.home, name='home'), 
]

Öffnen Sie nun die Datei .\python_tutorial\urls.py, und ersetzen Sie den Inhalt durch den folgenden Code:Finally, open the .\python_tutorial\urls.py file and replace the contents with the following.

Neuer Inhalt von .\python_tutorial\urls.pyNew contents of .\python_tutorial\urls.py

from django.conf.urls import url, include
from django.contrib import admin
from tutorial import views

urlpatterns = [
    # Invoke the home view in the tutorial app by default
    url(r'^$', views.home, name='home'),
    # Defer any URLS to the /tutorial directory to the tutorial app
    url(r'^tutorial/', include('tutorial.urls', namespace='tutorial')),
    url(r'^admin/', admin.site.urls),
]

Wenn Sie mit der Entwicklung in Django vertraut sind, ist dies nichts neu für Sie. Ansonsten haben wir nichts anderes getan, als Django mitzuteilen, wie Anforderungen an die Lernprogramm-App weitergeleitet werden sollen. Django sucht zuerst in der Datei .\python_tutorial\urls.py. Die neuen hinzugefügten Zeilen weisen Django an, Anforderungen an den Stamm an die Ansicht home in der Lernprogramm-App weiterzuleiten und alle Anforderungen an /tutorial/* an die Lernprogramm-App zu senden.If you're familiar with Django development, this isn't anything new for you. If not, all we've done here is tell Django how to route requests to the tutorial app. Django looks first in the .\python_tutorial\urls.py file. The new lines we added there tell it to redirect requests to the root to the home view in the tutorial app, and also tells it to send any requests to /tutorial/* to the tutorial app.

Die Einträge in der Datei .\tutorial\urls.py weisen Django an, Anforderungen entweder an /tutorial oder /tutorial/home an die Ansicht home zu senden. Die home-Funktion in .\tutorial\views.py gibt schließlich eine einfache HTTP-Antwort zurück.The entries in the .\tutorial\urls.py file tell Django to send requests to either /tutorial or /tutorial/home to the home view. Finally, the home function in .\tutorial\views.py returns a simple HTTP response.

Wenn Sie alle Ihre Änderungen speichern und zu http://localhost:8000 navigieren, sollte eine Willkommensmeldung angezeigt werden. Da wir nun bestätigt haben, dass die App funktioniert, können wir uns an die echte Arbeit machen.If you save all of your changes and navigate to http://localhost:8000 you should see "Welcome to the tutorial." Now that we've confirmed that the app is working, we're ready to do some real work.

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 nun damit, die statische Meldung durch etwas aussagekräftigeres zu ersetzen. Beginnen Sie, indem Sie ein neues Unterverzeichnis im Verzeichnis tutorial mit dem Namen templates erstellen. Erstellen Sie im Verzeichnis templates ein neues Unterverzeichnis mit dem Namen tutorial. Erstellen Sie schließlich eine neue Datei in diesem Verzeichnis mit dem Namen layout.html, und fügen Sie den folgenden Code hinzu. Dies ist ein Layout, das von allen Seiten in der App zum Hinzufügen von Bootstrap für das grundlegende Layout und die Formatierung und einer einfachen Navigationsleiste verwendet wird.Let's begin by replacing the static message with something nicer. Start by creating a new subdirectory in the tutorial directory called templates. In the templates directory, create a new subdirectory called tutorial. Finally, create a new file in this directory called layout.html, and add the following code. This will be a layout used by all pages in the app to add Bootstrap for basic layout and styling, and a simple nav bar.

Inhalt der Datei .\tutorial\templates\tutorial\layout.htmlContents of the .\tutorial\templates\tutorial\layout.html file

<!DOCTYPE html>
<html>
<head>
  <title>Python Outlook API Sample</title>
  <!-- Latest compiled and minified CSS -->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

  <!-- Optional theme -->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
</head>
<body style="padding-top: 70px;">
  <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="/">Python Outlook Sample</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="/tutorial/mail">Inbox</a></li>
          <li id='calendar-nav'><a href="/tutorial/events">Calendar</a></li>
          <li id='contacts-nav'><a href="/tutorial/contacts">Contacts</a></li>
        </ul>
      </div>
    </div>
  </nav>

  <div class="container main-container">
    {% block content %}{% endblock %}
  </div>

</body>
</html>

Als Nächstes werden wir eine einfache Startseite erstellen, um die Schaltfläche Verbindung mit Outlook herstellen anzuzeigen. Erstellen Sie eine Datei mit dem Namen home.html im Verzeichnis .\tutorial\templates\tutorial, und fügen Sie den folgenden Code hinzu.Now let's create a simple homepage to show a Connect to Outlook button. Create a file called home.html in the .\tutorial\templates\tutorial directory and add the following code.

Inhalt der Datei .\tutorial\templates\tutorial\home.htmlContents of the .\tutorial\templates\tutorial\home.html file

{% extends "tutorial/layout.html" %}
{% block content %}
<div class="jumbotron">
  <h1>Python Outlook Sample</h1>
  <p>This example shows how to get an OAuth token from Azure using the <a href="https://docs.microsoft.com/azure/active-directory/develop/active-directory-v2-protocols-oauth-code" target="_blank">authorization code 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="{{signin_url}}" role="button" id="connect-button">Connect to Outlook</a>
  </p>
</div>
{% endblock %}

Nun wollen wir die home-Funktion in .\tutorial\views.py so ändern, dass diese neue Ansicht verwendet wird. Aktualisieren Sie die home-Funktion so, dass sie dem folgenden Code entspricht.Finally, let's modify the home function in .\tutorial\views.py to use this new view. Update the home function to match the following.

Aktualisierte home-FunktionUpdated home function

def home(request):
  sign_in_url = '#'
  context = { 'signin_url': sign_in_url }
  return render(request, 'tutorial/home.html', context)

Wie Sie sehen, ist unsere Startseite sehr einfach. Im Moment führt der Link noch keine Aktion aus, das werden wir aber bald ändern.As you can see, our home page will be very simple. For now, the link doesn't do anything, but we'll fix that soon.

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 und einen geheimen Schlüssel abzurufen.Head over to the Application Registration Portal to quickly get an application ID and secret.

  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 python-tutorial für den Namen ein, und klicken Sie auf Anwendung erstellen.Click the Add an app button. Enter python-tutorial for the name and click Create application.
  3. Suchen Sie den Abschnitt Anwendungsgeheimnisse, und klicken Sie auf die Schaltfläche Neues Kennwort generieren. Kopieren Sie nun das Kennwort, und speichern Sie es an einem sicheren Ort. Nachdem Sie das Kennwort kopiert haben, klicken Sie auf Ok.Locate the Application Secrets section, and click the Generate New Password button. Copy the password now and save it to a safe place. Once you've copied the password, click Ok.
  4. Suchen Sie den Abschnitt Plattformen, und klicken Sie auf Plattform hinzufügen. Wählen Sie Web aus, und geben Sie dann http://localhost:8000/tutorial/gettoken/ unter Umleitungs-URIs ein.Locate the Platforms section, and click Add Platform. Choose Web, then enter http://localhost:8000/tutorial/gettoken/ under Redirect URIs.
  5. Klicken Sie auf Speichern, um die Registrierung abzuschließen. Kopieren Sie die App-ID, und speichern Sie sie zusammen mit dem Kennwort, das Sie zuvor kopiert haben. Wir benötigen diese Werte bald.Click Save to complete the registration. Copy the Application Id and save it along with the password you copied earlier. We'll need those values 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 ist es, den Link auf unserer Startseite so einzurichten, dass der OAuth2-Autorisierungscodegenehmigungs-Fluss mit Azure AD initiiert wird. Der Einfachheit halber verwenden wir die Bibliothek Requests: HTTP for Humans, um alle unsere HTTP-Anforderungen zu verarbeiten. Geben Sie an der Eingabeaufforderung den folgenden Befehl ein.Our goal in this section is to make the link on our home page initiate the OAuth2 Authorization Code Grant flow with Azure AD. To make things easier, we'll use the Requests: HTTP for Humans library to handle all of our HTTP requests. At your command prompt, enter the following command.

pip install requests

Jetzt ist die Bibliothek installiert und kann verwendet werden. Erstellen Sie eine neue Datei im tutorial-Verzeichnis mit dem Namen authhelper.py. Wir beginnen, indem wir zunächst eine Funktion zum Generieren der Anmelde-URL definieren.Now the library is installed and ready to use. Create a new file in the tutorial directory called authhelper.py. We'll start here by defining a function to generate the login URL.

Inhalt der Datei .\tutorial\authhelper.pyContents of the .\tutorial\authhelper.py file

from urllib.parse import quote, urlencode
import base64
import json
import time

# Client ID and secret
client_id = 'YOUR APP ID HERE'
client_secret = 'YOUR APP PASSWORD HERE'

# Constant strings for OAuth2 flow
# The OAuth authority
authority = 'https://login.microsoftonline.com'

# The authorize URL that initiates the OAuth2 client credential flow for admin consent
authorize_url = '{0}{1}'.format(authority, '/common/oauth2/v2.0/authorize?{0}')

# The token issuing endpoint
token_url = '{0}{1}'.format(authority, '/common/oauth2/v2.0/token')

# The scopes required by the app
scopes = [ 'openid',
           'User.Read',
           'Mail.Read' ]

def get_signin_url(redirect_uri):
  # Build the query parameters for the signin url
  params = { 'client_id': client_id,
             'redirect_uri': redirect_uri,
             'response_type': 'code',
             'scope': ' '.join(str(i) for i in scopes)
            }

  signin_url = authorize_url.format(urlencode(params))

  return signin_url

Ersetzen Sie die Platzhalter YOUR APP ID HERE und YOUR APP PASSWORD HERE durch die Werte, die Sie in Schritt 3 generiert haben, und speichern Sie die Änderungen.Replace the YOUR APP ID HERE and YOUR APP PASSWORD HERE placeholders with the values you generated in step 3 and save your changes.

Da wir nun über tatsächliche Werte für die Client-ID und den geheimen Clientschlüssel verfügen, können wir mit der neuen Funktion arbeiten. Ändern Sie die home-Funktion in der Datei .\tutorial\views.py so, dass die get_signin_url-Funktion verwendet wird, um den Link auszufüllen. Die Funktion verwendet einen Parameter, und zwar redirect_uri. Dieser Wert wird verwendet, um die URL in der App festzulegen, zu der Azure eine Umleitung ausführt, nachdem die Anmeldung abgeschlossen ist. Wir fahren nun fort und erstellen eine Platzhalteransicht, mit dem Namen gettoken, die als Umleitungsziel fungiert.Now that we have actual values for the client ID and secret, let's put our new function to work. Modify the home function in the .\tutorial\views.py file to use the get_signin_url function to fill in the link. That function takes a parameter, redirect_uri. This value is used to set the URL in our app where Azure will redirect after signin is complete. Let's go ahead and create a placeholder view to act as our redirect target called gettoken.

Aktualisierte Inhalte von .\tutorial\views.pyUpdated contents of .\tutorial\views.py

from django.shortcuts import render
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse
from tutorial.authhelper import get_signin_url

# Create your views here.

def home(request):
  redirect_uri = request.build_absolute_uri(reverse('tutorial:gettoken'))
  sign_in_url = get_signin_url(redirect_uri)
  return HttpResponse('<a href="' + sign_in_url +'">Click here to sign in and view your mail</a>')

def gettoken(request):
  return HttpResponse('gettoken view')

In der Ansicht werden derzeit nicht viele Aktionen ausgeführt, dies werden wir aber bald ändern. Fügen Sie diese neue Ansicht der Datei .\tutorials\urls.py hinzu.The view doesn't do much now, but we'll change that soon. Add this new view to the .\tutorials\urls.py file.

Aktualisierter Inhalt der Datei .\tutorials\urls.pyUpdated contents of the .\tutorials\urls.py file

from django.conf.urls import url 
from tutorial import views 

app_name = 'tutorial'
urlpatterns = [
  # The home view ('/tutorial/') 
  url(r'^$', views.home, name='home'), 
  # Explicit home ('/tutorial/home/') 
  url(r'^home/$', views.home, name='home'), 
  # Redirect to get token ('/tutorial/gettoken/')
  url(r'^gettoken/$', views.gettoken, name='gettoken'),
]

Speichern Sie Ihre Änderungen, und navigieren Sie zu http://localhost:8000. Wenn Sie den Mauszeiger über den Link bewegen, sollte dies folgendermaßen aussehen:Save your changes and browse to http://localhost:8000. If you hover over the link, it should look like:

https://login.microsoftonline.com/common/oauth2/v2.0/authorize?scope=openid+User.Read+Mail.Read&response_type=code&client_id=<SOME GUID>&redirect_uri=http%3A%2F%2Flocalhost%3A8000%2Ftutorial%2Fgettoken%2F

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. Der Browser sollte nun zurück an die Ansicht gettoken geleitet werden. Die Ansicht ist noch inaktiv, also lassen Sie uns das nun ändern.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 the gettoken view. The view doesn't do anything yet, so let's fix that now.

Austauschen des Codes durch ein TokenExchanging the code for a token

Als Erstes müssen wir den Autorisierungscode aus der Anforderung extrahieren. Wenn Azure eine Umleitung zu unserer gettoken-Funktion ausführt, ist ein code-Abfrageparameter enthalten, der den Autorisierungscode enthält. Aktualisieren Sie die gettoken-Funktion so, dass der Wert dieses Parameters abgerufen und angezeigt wird.The first thing we need to do is extract the authorization code from the request. When Azure redirects to our gettoken function, it includes a code query parameter, which contains the authorization code. Update the gettoken function to get this parameter's value and display it.

Aktualisierte gettoken-Funktion in .\tutorial\views.pyUpdated gettoken function in .\tutorial\views.py

def gettoken(request):
  auth_code = request.GET['code']
  return HttpResponse('Authorization code: {0}'.format(auth_code))

Speichern Sie Ihre Änderungen, und wiederholen Sie den Vorgang der Anmeldung bei der App. Anstelle einer statischen Meldung sollte nun der Wert des Autorisierungscodes auf dem Bildschirm angezeigt werden. Das ist zwar schon besser, aber immer noch nicht sehr hilfreich. Lassen Sie uns nun tatsächlich etwas mit dem Code ausführen.Save your changes and repeat the signin process in the app. Now instead of a static message, you should see the value of the authorization code printed on the screen. We're getting closer, but that's still not very useful. Let's actually do something with that code.

Importieren Sie zuerst die requests-Bibliothek, indem Sie die folgende Zeile oben in .\tutorial\authhelper.py hinzufügen.First import the requests library by adding the following line to the top of .\tutorial\authhelper.py.

import requests

Fügen Sie nun eine weitere Hilfsfunktion zu authhelper.py mit dem Namen get_token_from_code hinzu.Now add another helper function to authhelper.py called get_token_from_code.

get_token_from_code in der Datei .\tutorial\authhelper.pyget_token_from_code in the .\tutorial\authhelper.py file

def get_token_from_code(auth_code, redirect_uri):
  # Build the post form for the token request
  post_data = { 'grant_type': 'authorization_code',
                'code': auth_code,
                'redirect_uri': redirect_uri,
                'scope': ' '.join(str(i) for i in scopes),
                'client_id': client_id,
                'client_secret': client_secret
              }

  r = requests.post(token_url, data = post_data)

  try:
    return r.json()
  except:
    return 'Error retrieving token: {0} - {1}'.format(r.status_code, r.text)

Abrufen des BenutzersGetting the user

Bei der ersten Verwendung des Zugriffstokens rufen wir den Anzeigenamen des Benutzers von Microsoft Graph ab, um sicherzustellen, dass das Zugriffstoken funktioniert.Our first use of the access token will be to get the user's display name from Microsoft Graph, just to make sure that our access token works.

Erstellen Sie eine neue Datei im tutorial-Verzeichnis mit dem Namen outlookservice.py. In diese Datei implementieren wir alle Outlook-API-Funktionen. Wir beginnen, indem wir eine allgemeine Methode zum Senden von API-Anforderungen mit dem Namen make_api_call erstellen.Create a new file in the tutorial directory called outlookservice.py. We'll implement all of our Outlook API functions in this file. We'll start by creating a generic method for sending API requests called make_api_call.

Inhalt von ./tutorial/outlookservice.pyContents of ./tutorial/outlookservice.py

import requests
import uuid
import json

graph_endpoint = 'https://graph.microsoft.com/v1.0{0}'

# Generic API Sending
def make_api_call(method, url, token, payload = None, parameters = None):
  # Send these headers with all API calls
  headers = { 'User-Agent' : 'python_tutorial/1.0',
              'Authorization' : 'Bearer {0}'.format(token),
              'Accept' : 'application/json' }

  # Use these headers to instrument calls. Makes it easier
  # to correlate requests and responses in case of problems
  # and is a recommended best practice.
  request_id = str(uuid.uuid4())
  instrumentation = { 'client-request-id' : request_id,
                      'return-client-request-id' : 'true' }

  headers.update(instrumentation)

  response = None

  if (method.upper() == 'GET'):
      response = requests.get(url, headers = headers, params = parameters)
  elif (method.upper() == 'DELETE'):
      response = requests.delete(url, headers = headers, params = parameters)
  elif (method.upper() == 'PATCH'):
      headers.update({ 'Content-Type' : 'application/json' })
      response = requests.patch(url, headers = headers, data = json.dumps(payload), params = parameters)
  elif (method.upper() == 'POST'):
      headers.update({ 'Content-Type' : 'application/json' })
      response = requests.post(url, headers = headers, data = json.dumps(payload), params = parameters)

  return response

Diese Funktion verwendet die requests-Bibliothek zum Senden von API-Anforderungen. Sie legt einen Standardsatz von Headern für jede Anforderung fest, einschließlich der Clientinstrumentation.This function uses the requests library to send API requests. It sets a standard set of headers on each requests, including client instrumentation.

Als Nächstes erstellen wir eine Funktion, um die make_api_call-Funktion zum Abrufen des Benutzers zu verwenden. Fügen Sie eine Funktion mit dem Namen get_me zu outlookservice.py hinzu.Now let's create a function to make us of the make_api_call function to get the user. Add a function called get_me to outlookservice.py.

Die Funktion get_me in ./tutorial/outlookservice.pyThe get_me function in ./tutorial/outlookservice.py

def get_me(access_token):
  get_me_url = graph_endpoint.format('/me')

  # Use OData query parameters to control the results
  #  - Only return the displayName and mail fields
  query_parameters = {'$select': 'displayName,mail'}

  r = make_api_call('GET', get_me_url, access_token, "", parameters = query_parameters)

  if (r.status_code == requests.codes.ok):
    return r.json()
  else:
    return "{0}: {1}".format(r.status_code, r.text)

Nun möchten wir sicherstellen, dass diese funktioniert. Ändern Sie die gettoken-Funktion views.py so, dass die folgenden Hilfsfunktionen verwendet und die Rückgabewerte angezeigt werden.Let's make sure that works. Modify the gettoken function in views.py to use these helper functions and display the return values.

Aktualisierte gettoken-Funktion in .\tutorial\views.pyUpdated gettoken function in .\tutorial\views.py

# Add import statement to include new function
from tutorial.outlookservice import get_me

def gettoken(request):
  auth_code = request.GET['code']
  redirect_uri = request.build_absolute_uri(reverse('tutorial:gettoken'))
  token = get_token_from_code(auth_code, redirect_uri)
  access_token = token['access_token']
  user = get_me(access_token)

  # Save the token in the session
  request.session['access_token'] = access_token
  return HttpResponse('User: {0}, Access token: {1}'.format(user['displayName'], access_token))

Wenn Sie Ihre Änderungen speichern, den Server neu starten und den Anmeldevorgang erneut durchlaufen, sollte eine lange Zeichenfolge von scheinbar unsinnigen Zeichen angezeigt werden. Wenn alles nach Plan läuft, sollte dies ein Zugriffstoken sein.If you save your changes, restart the server, and go through the sign-in process again, you should now see a long string of seemingly nonsensical characters. If everything's gone according to plan, that should be an access token.

Aktualisieren des ZugriffstokensRefreshing the access token

Von Azure zurückgegebene Zugriffstoken sind eine Stunde lang gültig. Wenn Sie das Token verwenden, nachdem es abgelaufen ist, geben die API-Aufrufe 401-Fehler zurück. Sie könnten den Benutzer bitten, sich erneut anzumelden, aber die bessere Option besteht darin, das Token automatisch zu aktualisieren.Access tokens returned from Azure are valid for an hour. If you use the token after it has expired, the API calls will return 401 errors. You could ask the user to sign in again, but the better option is to refresh the token silently.

Zu diesem Zweck muss die App den offline_access-Bereich anfordern. Fügen Sie diesen Bereich dem scopes-Array in authhelper.py hinzu:In order to do that, the app must request the offline_access scope. Add this scope to the scopes array in authhelper.py:

# The scopes required by the app
scopes = [ 'openid',
           'offline_access',
           'User.Read',
           'Mail.Read' ]

Dies bewirkt, dass die Tokenantwort von Azure ein Aktualisierungstoken enthält. Wir werden nun gettoken in views.py so aktualisieren, dass das Aktualisierungstoken und die Ablaufzeit in der Sitzung gespeichert werden.This will cause the token response from Azure to include a refresh token. Let's update gettoken in views.py to save the refresh token and the expiration time in the session.

Aktualisierte gettoken-Funktion in .\tutorial\views.pyUpdated gettoken function in .\tutorial\views.py

# Add import statement to include new function
from tutorial.outlookservice import get_me

def gettoken(request):
  auth_code = request.GET['code']
  redirect_uri = request.build_absolute_uri(reverse('tutorial:gettoken'))
  token = get_token_from_code(auth_code, redirect_uri)
  access_token = token['access_token']
  user = get_me(access_token)
  refresh_token = token['refresh_token']
  expires_in = token['expires_in']

  # expires_in is in seconds
  # Get current timestamp (seconds since Unix Epoch) and
  # add expires_in to get expiration time
  # Subtract 5 minutes to allow for clock differences
  expiration = int(time.time()) + expires_in - 300

  # Save the token in the session
  request.session['access_token'] = access_token
  request.session['refresh_token'] = refresh_token
  request.session['token_expires'] = expiration
  return HttpResponse('User: {0}, Access token: {1}'.format(user['displayName'], access_token))

Lassen Sie uns nun eine Funktion zum Aktualisieren des Zugriffstokens erstellen. Fügen Sie die folgende Funktion zu authhelper.py hinzu:Now let's create a function to refresh the access token. Add the following function to authhelper.py.

Die Funktion get_token_from_refresh_token in ./tutorial/authhelper.pyThe get_token_from_refresh_token function in ./tutorial/authhelper.py

def get_token_from_refresh_token(refresh_token, redirect_uri):
  # Build the post form for the token request
  post_data = { 'grant_type': 'refresh_token',
                'refresh_token': refresh_token,
                'redirect_uri': redirect_uri,
                'scope': ' '.join(str(i) for i in scopes),
                'client_id': client_id,
                'client_secret': client_secret
              }

  r = requests.post(token_url, data = post_data)

  try:
    return r.json()
  except:
    return 'Error retrieving token: {0} - {1}'.format(r.status_code, r.text)

Jetzt erstellen wir abschließen eine Hilfsfunktion, um das Zugriffstoken abzurufen. Die Funktion überprüft den Ablaufzeitpunkt und aktualisiert diesen, falls das Token abgelaufen ist. Andernfalls wird lediglich das Zugriffstoken aus der Sitzung zurückgegeben. Fügen Sie die folgende Funktion zu authhelper.py hinzu:Finally let's create a helper function to retrieve the access token. The function will check the expiration time, and if the token is expired, will refresh it. Otherwise it will just return the access token from the session. Add the following function to authhelper.py.

Die Funktion get_access_token in ./tutorial/authhelper.pyThe get_access_token function in ./tutorial/authhelper.py

def get_access_token(request, redirect_uri):
  current_token = request.session['access_token']
  expiration = request.session['token_expires']
  now = int(time.time())
  if (current_token and now < expiration):
    # Token still valid
    return current_token
  else:
    # Token expired
    refresh_token = request.session['refresh_token']
    new_tokens = get_token_from_refresh_token(refresh_token, redirect_uri)

    # Update session
    # expires_in is in seconds
    # Get current timestamp (seconds since Unix Epoch) and
    # add expires_in to get expiration time
    # Subtract 5 minutes to allow for clock differences
    expiration = int(time.time()) + new_tokens['expires_in'] - 300

    # Save the token in the session
    request.session['access_token'] = new_tokens['access_token']
    request.session['refresh_token'] = new_tokens['refresh_token']
    request.session['token_expires'] = expiration

    return new_tokens['access_token']

Importieren Sie nun get_access_token in views.py, damit wir dieses verwenden können.Let's import get_access_token in views.py so we can make use of it.

from tutorial.authhelper import get_signin_url, get_token_from_code, get_access_token

Verwenden der Mail-APIUsing the Mail API

Da wir nun ein Zugriffstoken abrufen können, bietet es sich an, mit der Mail-API fortzufahren. Beginnen wir mit dem Erstellen einer mail-Ansicht in views.py.Now that we can get an access token, we're in a good position to do something with the Mail API. Let's start by creating a mail view in views.py.

mail-Funktion in .\tutorial\views.pymail function in .\tutorial\views.py

def mail(request):
  access_token = get_access_token(request, request.build_absolute_uri(reverse('tutorial:gettoken')))
  # If there is no token in the session, redirect to home
  if not access_token:
    return HttpResponseRedirect(reverse('tutorial:home'))
  else:
    return HttpResponse('Access token found in session: {0}'.format(access_token))

Aktualisieren Sie die Datei urls.py so, dass ein Eintrag für die neue Ansicht eingeschlossen wird.Update the urls.py file to include an entry for the new view.

Aktualisierte Inhalte von .\tutorial\urls.pyUpdated contents of .\tutorial\urls.py

from django.conf.urls import url
from tutorial import views 

app_name = 'tutorial'
urlpatterns = [ 
  # The home view ('/tutorial/') 
  url(r'^$', views.home, name='home'), 
  # Explicit home ('/tutorial/home/') 
  url(r'^home/$', views.home, name='home'), 
  # Redirect to get token ('/tutorial/gettoken/')
  url(r'^gettoken/$', views.gettoken, name='gettoken'),
  # Mail view ('/tutorial/mail/')
  url(r'^mail/$', views.mail, name='mail'),
]

Aktualisieren Sie die gettoken-Funktion so, dass eine Umleitung an die mail-Ansicht erfolgt, nachdem das Token in der Sitzung gespeichert wurde.Update the gettoken function to redirect to the mail view after saving the token in the session.

Aktualisierte gettoken-Funktion in .\tutorial\views.pyUpdated gettoken function in .\tutorial\views.py

def gettoken(request):
  auth_code = request.GET['code']
  redirect_uri = request.build_absolute_uri(reverse('tutorial:gettoken'))
  token = get_token_from_code(auth_code, redirect_uri)
  access_token = token['access_token']
  user = get_me(access_token)
  refresh_token = token['refresh_token']
  expires_in = token['expires_in']

  # expires_in is in seconds
  # Get current timestamp (seconds since Unix Epoch) and
  # add expires_in to get expiration time
  # Subtract 5 minutes to allow for clock differences
  expiration = int(time.time()) + expires_in - 300

  # Save the token in the session
  request.session['access_token'] = access_token
  request.session['refresh_token'] = refresh_token
  request.session['token_expires'] = expiration
  return HttpResponseRedirect(reverse('tutorial:mail'))

Derzeit liest diese Funktion das Token lediglich aus dem Cookie und zeigt es an. Speichern Sie Ihre Änderungen, starten Sie den Server neu, und durchlaufen Sie den Anmeldevorgang erneut. Das Token sollte angezeigt werden. Da wir nun wissen, dass wir Zugriff auf das Token in der mail-Funktion haben, können wir die Mail-API aufrufen.For now all this does is read the token back from the cookie and display it. Save your changes, restart the server, and go through the signon process again. You should see the token displayed. Now that we know we have access to the token in the mail function, we're ready to call the Mail API.

Wir fügen nun eine Funktion hinzu, die die make_api_call-Funktion verwendet, um eine Anforderung zum Abrufen von Nachrichten aus dem Posteingang zu implementieren. Erstellen Sie eine neue Funktion in outlookservice.py mit dem Namen get_my_messages.Now let's add a function that will use the make_api_call function to implement a request to retrieve messages from the inbox. Create a new function in outlookservice.py called get_my_messages.

Die Funktion get_my_messages in ./tutorial/outlookservice.pyThe get_my_messages function in ./tutorial/outlookservice.py

def get_my_messages(access_token):
  get_messages_url = graph_endpoint.format('/me/mailfolders/inbox/messages')

  # Use OData query parameters to control the results
  #  - Only first 10 results returned
  #  - Only return the ReceivedDateTime, Subject, and From fields
  #  - Sort the results by the ReceivedDateTime field in descending order
  query_parameters = {'$top': '10',
                      '$select': 'receivedDateTime,subject,from',
                      '$orderby': 'receivedDateTime DESC'}

  r = make_api_call('GET', get_messages_url, access_token, parameters = query_parameters)

  if (r.status_code == requests.codes.ok):
    return r.json()
  else:
    return "{0}: {1}".format(r.status_code, r.text)

Nun können wir die mail-Funktion so ändern, dass diese Funktion aufgerufen und E-Mails abgerufen werden. Importieren Sie zuerst die get_my_messages-Funktion, indem Sie die folgende Zeile oben in views.py hinzufügen.Now we can modify the mail function to call this function and retrieve email. First, import the get_my_messages function by adding the following line to the top of views.py.

from tutorial.outlookservice import get_me, get_my_messages

Aktualisieren Sie dann die mail-Funktion, um die neue Funktion aufrufen.Then update the mail function to call the new function.

Neue Version der mail-Funktion in ./tutorial/views.pyNew version of the mail function in ./tutorial/views.py

def mail(request):
  access_token = get_access_token(request, request.build_absolute_uri(reverse('tutorial:gettoken')))
  # If there is no token in the session, redirect to home
  if not access_token:
    return HttpResponseRedirect(reverse('tutorial:home'))
  else:
    messages = get_my_messages(access_token)
    return HttpResponse('Messages: {0}'.format(messages))

Wenn Sie die Änderungen speichern und sich bei der App anmelden, sollte nun eine unformatierte Auflistung der JSON-Antwort angezeigt werden.If you save the changes and sign into the app, you should now see a raw listing of the JSON response.

Anzeigen der ErgebnisseDisplaying the results

Während die aktuelle Auflistung von Nachrichten bestätigt, dass die API-Aufrufe funktionieren, können wir die Django-Vorlagen verwenden, um die Ergebnisse in einer benutzerfreundlicheren Weise anzuzeigen.While the current listing of messages confirms that the API calls are working, we can use Django templates to display the results in a more user-friendly fashion.

Erstellen Sie eine neue Datei im Verzeichnis ./tutorial/templates/tutorial mit dem Namen mail.html, und fügen Sie den folgenden Code hinzu.Create a new file in the ./tutorial/templates/tutorial directory called mail.html, and add the following code.

Inhalt der Datei ./tutorial/templates/tutorial/mail.htmlContents of the ./tutorial/templates/tutorial/mail.html file

{% extends "tutorial/layout.html" %}
{% block content %}
<h1>Your Email</h1>
<table class="table">
  <tr>
    <th>From</th>
    <th>Subject</th>
    <th>Received</th>
  </tr>

  {% for message in messages %}
    <tr>
      <td>{{ message.from.emailAddress.name }}</td>
      <td>{{ message.subject }}</td>
      <td>{{ message.receivedDateTime }}</td>
    </tr>
  {% endfor %}
</table>
{% endblock %}

Aktualisieren Sie die mail -Funktion in views.py so, dass diese neue Vorlage verwendet wird.Update the mail function in views.py to use this new template.

Aktualisierte mail-Funktion in ./tutorial/views.pyUpdated mail function in ./tutorial/views.py

def mail(request):
  access_token = get_access_token(request, request.build_absolute_uri(reverse('tutorial:gettoken')))
  # If there is no token in the session, redirect to home
  if not access_token:
    return HttpResponseRedirect(reverse('tutorial:home'))
  else:
    messages = get_my_messages(access_token)
    context = { 'messages': messages['value'] }
    return render(request, 'tutorial/mail.html', context)

Speichern Sie die Änderungen, und melden Sie sich bei der App an. Nun sollte eine einfache Tabelle von Nachrichten in Ihrem Posteingang angezeigt werden.Save the changes and sign in to the app. You should now see a simple table of messages in your inbox.

Die fertige App, in der der Posteingang des Benutzers angezeigt wird.

Hinzufügen von Kalender- und Kontakte-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. Starten Sie den Browser unbedingt neu, 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 to restart your browser 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 das scopes-Array in authhelper.py, um den Calendars.Read-Bereich einzuschließen.Update the scopes array in authhelper.py to include the Calendars.Read scope.

    # The scopes required by the app
    scopes = [ 'openid', 
               'offline_access',
               'User.Read',
               'Mail.Read',
               'Calendars.Read' ]
    
  2. Fügen Sie eine neue Funktion zu outlookservice.py mit dem Namen get_my_events hinzu.Add a new function to outlookservice.py called get_my_events.

    def get_my_events(access_token):
      get_events_url = graph_endpoint.format('/me/events')
    
      # Use OData query parameters to control the results
      #  - Only first 10 results returned
      #  - Only return the Subject, Start, and End fields
      #  - Sort the results by the Start field in ascending order
      query_parameters = {'$top': '10',
                          '$select': 'subject,start,end',
                          '$orderby': 'start/dateTime ASC'}
    
      r = make_api_call('GET', get_events_url, access_token, parameters = query_parameters)
    
      if (r.status_code == requests.codes.ok):
        return r.json()
      else:
        return "{0}: {1}".format(r.status_code, r.text)
    
  3. Importieren Sie in views.py die get_my_events-Funktion.In views.py, import the get_my_events function.

    from tutorial.outlookservice import get_my_events
    
  4. Fügen Sie eine neue Ansicht zu views.py mit dem Namen events hinzu.Add a new view to views.py called events.

    def events(request):
      access_token = get_access_token(request, request.build_absolute_uri(reverse('tutorial:gettoken')))
      # If there is no token in the session, redirect to home
      if not access_token:
        return HttpResponseRedirect(reverse('tutorial:home'))
      else:
        events = get_my_events(access_token)
        context = { 'events': events['value'] }
        return render(request, 'tutorial/events.html', context)
    
  5. Fügen Sie die neue Ansicht zu ./tutorial/urls.py hinzu.Add the new view to ./tutorial/urls.py.

    # Events view ('/tutorial/events/')
    url(r'^events/$', views.events, name='events'),
    
  6. Fügen Sie eine neue events.html-Vorlage zu ./tutorial/templates/tutorial directory hinzu.Add a new template events.html in the ./tutorial/templates/tutorial directory.

    {% extends "tutorial/layout.html" %}
    {% block content %}
    <h1>Your Events</h1>
    <table class="table">
      <tr>
        <th>Subject</th>
        <th>Start</th>
        <th>End</th>
      </tr>
    
      {% for event in events %}
        <tr>
          <td>{{ event.subject }}</td>
          <td>{{ event.start.dateTime }} ({{ event.start.timeZone }})</td>
          <td>{{ event.end.dateTime }} ({{ event.end.timeZone }})</td>
        </tr>
      {% endfor %}
    </table>
    {% endblock %}
    
  7. Speichern Sie alle Änderungen speichern. Navigieren Sie zu http://localhost:8000/tutorial/events, nachdem Sie sich bei der App angemeldet haben.Save all changes. After signing into the app, browse to http://localhost:8000/tutorial/events.

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

  1. Aktualisieren Sie das scopes-Array in authhelper.py, um den Contacts.Read-Bereich einzuschließen.Update the scopes array in authhelper.py to include the Contacts.Read scope.

    # The scopes required by the app
    scopes = [ 'openid', 
               'offline_access',
               'User.Read',
               'Mail.Read',
               'Contacts.Read' ]
    
  2. Fügen Sie eine neue Funktion zu outlookservice.py mit dem Namen get_my_contacts hinzu.Add a new function to outlookservice.py called get_my_contacts.

    def get_my_contacts(access_token):
      get_contacts_url = graph_endpoint.format('/me/contacts')
    
      # Use OData query parameters to control the results
      #  - Only first 10 results returned
      #  - Only return the GivenName, Surname, and EmailAddresses fields
      #  - Sort the results by the GivenName field in ascending order
      query_parameters = {'$top': '10',
                          '$select': 'givenName,surname,emailAddresses',
                          '$orderby': 'givenName ASC'}
    
      r = make_api_call('GET', get_contacts_url, access_token, parameters = query_parameters)
    
      if (r.status_code == requests.codes.ok):
        return r.json()
      else:
        return "{0}: {1}".format(r.status_code, r.text)
    
  3. Importieren Sie in views.py die get_my_contacts-Funktion.In views.py, import the get_my_contacts function.

    from tutorial.outlookservice import get_my_contacts
    
  4. Fügen Sie eine neue Ansicht zu views.py mit dem Namen contacts hinzu.Add a new view to views.py called contacts.

    def contacts(request):
      access_token = get_access_token(request, request.build_absolute_uri(reverse('tutorial:gettoken')))
      # If there is no token in the session, redirect to home
      if not access_token:
        return HttpResponseRedirect(reverse('tutorial:home'))
      else:
        contacts = get_my_contacts(access_token)
        context = { 'contacts': contacts['value'] }
        return render(request, 'tutorial/contacts.html', context)
    
  5. Fügen Sie die neue Ansicht zu ./tutorial/urls.py hinzu.Add the new view to ./tutorial/urls.py.

    # Contacts view ('/tutorial/contacts/')
    url(r'^contacts/$', views.contacts, name='contacts'),
    
  6. Fügen Sie eine neue contacts.html-Vorlage zu ./tutorial/templates/tutorial directory hinzu.Add a new template contacts.html in the ./tutorial/templates/tutorial directory.

    {% extends "tutorial/layout.html" %}
    {% block content %}
    <h1>Your Contacts</h1>
    <table class="table">
      <tr>
        <th>First Name</th>
        <th>Last Name</th>
        <th>Email Address</th>
      </tr>
    
      {% for contact in contacts %}
        <tr>
          <td>{{ contact.givenName }}</td>
          <td>{{ contact.surname }}</td>
          <td>{{ contact.emailAddresses.0.address }}</td>
        </tr>
      {% endfor %}
    </table>
    {% endblock %}
    
  7. Speichern Sie alle Änderungen speichern. Navigieren Sie zu http://localhost:8000/tutorial/contacts, nachdem Sie sich bei der App angemeldet haben.Save all changes. After signing into the app, browse to http://localhost:8000/tutorial/contacts.