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

In diesem Leitfaden werden Sie schrittweise durch den Prozess des Erstellens einer einfachen Ruby on Rails-App zum Zugreifen auf Benutzerdaten 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 Ruby on Rails app that accesses a user's data 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.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.This guide will use the Microsoft Graph to access Outlook mail. Microsoft recommends using the 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. 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.

In diesem Leitfaden wird davon ausgegangen, dass Sie Ruby on Rails bereits installiert haben und auf Ihrem Entwicklungscomputer ausführen.This guide assumes that you already have Ruby on Rails installed and working on your development machine.

Erstellen der AppCreate the app

Wechseln Sie über die Befehlszeile zum Verzeichnis, in dem Sie Ihre neue Ruby on Rails-App erstellen möchten. Führen Sie den folgenden Befehl aus, um eine App mit dem Namen o365-tutorial zu erstellen.From your command line, change your directory to a directory where you want to create your new Ruby on Rails app. Run the following command to create an app called o365-tutorial.

Hinweis

Sie können den Namen beliebig ändern. Für die Zwecke dieses Leitfadens gehe ich davon aus, dass der Name der App o365-tutorial ist.Feel free to change the name to whatever you want. For the purposes of this guide I will assume the name of the app is o365-tutorial.

rails new o365-tutorial

Wenn Sie mit Ruby on Rails vertraut sind, ist dies nichts Neues für Sie. Wenn Sie noch nicht mit Ruby on Rails vertraut sind, werden Sie bemerken, dass mit dem Befehl ein o365-tutorial-Unterverzeichnis erstellt wird, das eine Anzahl von Dateien und Verzeichnissen enthält. Die meisten davon sind nicht wichtig für unsere Zwecke, machen Sie sich also nicht zu viele Gedanken darüber.If you're familiar with Ruby on Rails, this is nothing new for you. If you're new to it, you'll notice that command creates an o365-tutorial sub-directory, which contains a number of files and directories. Most of these aren't important for our purposes, so don't worry too much about them.

Ändern Sie in der Befehlszeile Ihr Verzeichnis zum o365-tutorial-Unterverzeichnis. Stellen wir noch kurz sicher, dass die App erfolgreich erstellt wurde. Führen Sie den folgenden Befehl aus:On the command line, change your directory to the o365-tutorial sub-directory. Let's take a quick detour to verify that the app was created successfully. Run the following command:

rails server

Öffnen Sie einen Browser, und navigieren Sie zu http://localhost:3000. Es sollte die standardmäßige Startseite von Ruby on Rails angezeigt werden.Open a browser and navigate to http://localhost:3000. You should see the default Ruby on Rails welcome page.

Standardmäßige Startseite von Ruby on Rails.

Da wir nun sichergestellt haben, dass Ruby on Rails funktioniert, können wir uns an die echte Arbeit machen.Now that we've confirmed that Ruby on Rails 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.

Als Erstes ändern wir das 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. Öffnen Sie die Datei .\o365-tutorial\app\views\layouts\application.html.erb im bevorzugten Editor, und ersetzen Sie den Inhalt durch den folgenden Code.First let's modify the layout used by all pages in the app to add Bootstrap for basic layout and styling, and a simple nav bar. Open the .\o365-tutorial\app\views\layouts\application.html.erb file in your favorite editor and replace its contents with the following code.

Inhalt der Datei .\o365-tutorial\app\views\layouts\application.html.erbContents of the .\o365-tutorial\app\views\layouts\application.html.erb file

<!DOCTYPE html>
<html>
<head>
  <title>O365Tutorial</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">
  <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true %>
  <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
  <%= csrf_meta_tags %>
</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 Ruby 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="/mail/index">Inbox</a></li>
          <li id='calendar-nav'><a href="/calendar/index">Calendar</a></li>
          <li id='contacts-nav'><a href="/contacts/index">Contacts</a></li>
        </ul>
      </div>
    </div>
  </nav>

  <div class="container main-container">
<%= yield %>
  </div>

</body>
</html>

Im Folgenden werden einige kleine Änderungen an der CSS-Datei der Anwendung vorgenommen, um die hinzugefügte Navigationsleiste anzupassen. Öffnen Sie die Datei .\o365-tutorial\app\assets\stylesheets\application.css, und fügen Sie den folgenden Code hinzu.Let's also add a little change to the application's CSS file to accomodate the nav bar we added. Open the .\o365-tutorial\app\assets\stylesheets\application.css file and add the following code.

Der Code ist in der Datei .\o365-tutorial\app\assets\stylesheets\application.css vorhanden.New code in the .\o365-tutorial\app\assets\stylesheets\application.css file

body {
  padding-top: 70px;
}

Ersetzen Sie nun die standardmäßige Startseite durch eine eigene Seite. Dazu wird ein neuer Controller für die Startseite erstellt. Führen Sie in der Befehlszeile den folgenden Befehl aus:Now let's replace the default welcome page with a page of our own. To do that, we'll create a new controller for the home page. On the command line, run the following command:

rails generate controller Home

Wenn dieser Befehl abgeschlossen ist, erstellen Sie eine neue Datei in dem .\o365-tutorial\app\views\home-Verzeichnis namens index.html.erb. Fügen Sie den folgenden Code ein.Once that command is complete, create a new file in the .\o365-tutorial\app\views\home directory called index.html.erb. Paste in the following code.

Inhalt der Datei .\o365-tutorial\app\views\home\index.html.erbContents of the .\o365-tutorial\app\views\home\index.html.erb file

<div class="jumbotron">
  <h1>Outlook Ruby Sample</h1>
  <p>This example shows how to get an OAuth token from Azure using the <a href="https://docs.microsoft.com/en-us/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="<%= @login_url %>" role="button" id="connect-button">Connect to Outlook</a>
  </p>
</div>

Wie Sie sehen, ist unsere Startseite sehr einfach. Im Moment führt der Link noch keine Aktion aus, das werden wir aber bald ändern. Zuerst müssen wir Rails mitteilen, dass diese Aktion ausgelöst werden soll. Dazu müssen wir eine Route definieren. Öffnen Sie die Datei .\o365-tutorial\config\routes.rb, und legen Sie die standardmäßige Route (oder „Stamm“) auf die index-Aktion des home-Controllers fest, die wir gerade erstellt haben.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. First we need to tell Rails to invoke this action. To do that, we need to define a route. Open the .\o365-tutorial\config\routes.rb file, and set the default route (or "root") to the index action of the home controller we just created.

Inhalt der Datei .\o365-tutorial\config\routes.rbContents of the .\o365-tutorial\config\routes.rb file

Rails.application.routes.draw do
  root 'home#index'
end

Speichern Sie Ihre Änderungen. Beim Aufrufen von http://localhost:3000 sollte nun Folgendes angezeigt werden:Save your changes. Now browsing to http://localhost:3000 should look like:

Startseite der App.

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 o365-tutorial für den Namen ein, und klicken Sie auf Anwendung erstellen.Click the Add an app button. Enter o365-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:3000/authorize unter Umleitungs-URIs ein.Locate the Platforms section, and click Add Platform. Choose Web, then enter http://localhost:3000/authorize 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 den Gem oauth2 für das Verarbeiten unserer OAuth-Anforderungen. Des Weiteren verwenden wir den Gem activerecord-session_store zum Speichern unserer Sitzungen in einer Datenbank. Öffnen Sie ./o365-tutorial/GemFile, und fügen Sie die folgenden Zeilen an einer beliebigen Stelle in dieser Datei hinzu: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 oauth2 gem to handle our OAuth requests. We'll also use the activerecord-session_store gem to store our sessions in a database. Open the ./o365-tutorial/GemFile and add the following lines anywhere in that file:

gem 'oauth2'
gem 'activerecord-session_store'

Speichern Sie die Datei, und führen Sie den folgenden Befehl aus (starten Sie anschließend den Rails-Server neu):Save the file and run the following command (restart the rails server afterwards):

bundle install

Im Folgenden wird die App für die Verwendung des activerecord-session_store-Gem für den Sitzungsspeicher konfiguriert. Grund hierfür ist, dass der standardmäßige Cookiespeicher auf 4 KB Daten beschränkt ist, was nicht für das Speichern der Token ausreicht, die wir von Azure erhalten.Now let's configure the app to use the activerecord-session_store gem for session storage. The reason for this is that the default cookie store is limited to 4KB of data, which isn't enough for us to store the tokens we'll get back from Azure.

Öffnen Sie die Datei .\o365-tutorial\config\initializers\session_store.rb. Ersetzen Sie den Text :cookie_store mit :active_record_store.Open the .\o365-tutorial\config\initializers\session_store.rb file. Replace the text :cookie_store with :active_record_store.

Geben Sie in der Befehlszeile die folgenden Befehle ein, um die Sitzungsdatenbank zu erstellen.On the command line, enter the following commands to generate the session database.

rails generate active_record:session_migration
rails db:migrate

Aufgrund der Art des OAuth2-Flusses ist es sinnvoll, einen Controller zum Verarbeiten der Umleitungen aus Azure zu erstellen. Führen Sie den folgenden Befehl aus, um einen Controller mit dem Namen Auth zu generieren:Because of the nature of the OAuth2 flow, it makes sense to create a controller to handle the redirects from Azure. Run the following command to generate a controller named Auth:

rails generate controller Auth

Öffnen Sie die Datei .\o365-tutorial\app\helpers\auth_helper.rb. Wir beginnen, indem wir zunächst eine Funktion zum Generieren der Anmelde-URL definieren.Open the .\o365-tutorial\app\helpers\auth_helper.rb file. We'll start here by defining a function to generate the login URL.

Inhalt der Datei .\o365-tutorial\app\helpers\auth_helper.rbContents of the .\o365-tutorial\app\helpers\auth_helper.rb file

module AuthHelper

  # App's client ID. Register the app in Application Registration Portal to get this value.
  CLIENT_ID = '<YOUR APP ID HERE>'
  # App's client secret. Register the app in Application Registration Portal to get this value.
  CLIENT_SECRET = '<YOUR APP PASSWORD HERE>'

  # Scopes required by the app
  SCOPES = [ 'openid',
             'profile',
             'User.Read',
             'Mail.Read' ]

  REDIRECT_URI = 'http://localhost:3000/authorize' # Temporary!

  # Generates the login URL for the app.
  def get_login_url
    client = OAuth2::Client.new(CLIENT_ID,
                                CLIENT_SECRET,
                                :site => 'https://login.microsoftonline.com',
                                :authorize_url => '/common/oauth2/v2.0/authorize',
                                :token_url => '/common/oauth2/v2.0/token')

    login_url = client.auth_code.authorize_url(:redirect_uri => REDIRECT_URI, :scope => SCOPES.join(' '))
  end
end

Als Erstes definieren wir die Client-ID und den geheimen Clientschlüssel sowie die Berechtigungsbereiche, die unsere App erfordert. Wir werden einen Umleitungs-URI als hartcodierten Wert definieren. Wir werden später weitere Verbesserungen daran vornehmen, für unsere Zwecke reicht es jedoch aus.The first thing we do here is define our client ID and secret, and the permission scopes our app requires. We also define a redirect URI as a hard-coded value. We'll improve on that in a bit, but it will serve our purpose for now.

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.

Nachdem wir die get_login_url-Funktion implementiert haben, können wir mit dieser arbeiten. Öffnen Sie die Datei .\o365-tutorial\app\controllers\home_controller.rb, und ersetzen Sie den Inhalt durch den folgenden Code:Now that we've implemented the get_login_url function, let's put it to work. Open the .\o365-tutorial\app\controllers\home_controller.rb file and replace its contents with the following code:

Aktualisierter Inhalt der Datei .\o365-tutorial\app\controllers\home_controller.rbUpdated contents of the .\o365-tutorial\app\controllers\home_controller.rb file

class HomeController < ApplicationController
  include AuthHelper

  def index
    # Display the login link.
    @login_url = get_login_url
  end
end

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

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

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-Konto an. Ihr Browser sollte nun zurück zu unserer App geleitet werden, und es sollte der folgende Fehler angezeigt werden: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 account. Your browser should redirect to back to our app, and you should see a lovely error:

No route matches [GET] "/authorize"

Wenn Sie auf der Fehlerseite von Rails nach unten scrollen, werden die Anforderungsparameter angezeigt, die den Autorisierungscode enthalten.If you scroll down on Rails' error page, you can see the request parameters, which include the authorization code.

Parameters:
{"code"=>"M2ff0cb19-ec9d-db94-c5ab-4c634e319315"}

Grund für diesen Fehler ist, dass wir noch keine Route für das Verarbeiten des /authorize-Pfads implementiert haben, den wir als unseren Umleitungs-URI hartcodiert haben. Rails hat uns gezeigt, dass wird den Autorisierungscode in der Anforderung erneut erhalten. Wir sind also auf dem richtigen Weg. Im Folgenden werden wir diesen Fehler beheben.The reason we're seeing the error is because we haven't implemented a route to handle the /authorize path we hard-coded as our redirect URI. However, Rails has shown us that we're getting the authorization code back in the request, so we're on the right track! Let's fix that error now.

Austauschen des Codes durch ein TokenExchanging the code for a token

Zunächst fügen wir eine Route für den /authorize-Pfad zu routes.rb hinzu.First, let's add a route for the /authorize path to routes.rb.

Aktualisierter Inhalt der Datei .\o365-tutorial\config\routes.rbUpdated contents of the .\o365-tutorial\config\routes.rb file

Rails.application.routes.draw do
  root 'application#home'
  get 'authorize' => 'auth#gettoken'
end

Die hinzugefügte Zeile gibt Rails an, dass wenn eine GET-Anforderung für /authorize hereinkommt, die gettoken-Aktion auf dem auth-Controller ausgelöst wird. Damit dies funktioniert, müssen wir also diese Aktion implementieren. Öffnen Sie die Datei .\o365-tutorial\app\controllers\auth_controller.rb, und definieren Sie die gettoken-Aktion.The added line tells Rails that when a GET request comes in for /authorize, invoke the gettoken action on the auth controller. So to make this work, we need to implement that action. Open the .\o365-tutorial\app\controllers\auth_controller.rb file and define the gettoken action.

Inhalt der Datei .\o365-tutorial\app\controllers\auth_controller.rbContents of the .\o365-tutorial\app\controllers\auth_controller.rb file

class AuthController < ApplicationController
  include AuthHelper

  def gettoken
    render text: params[:code]
  end
end

Im Folgenden nehmen wir eine letzte Verfeinerung vor, bevor wir diesen neuen Code verwenden. Da wir nun über eine Route für den Umleitungs-URI verfügen, können wir die hartcodierte Konstante in auth_helper.rb und stattdessen den folgenden Rails-Namen für die Route verwenden: authorize_url.Let's make one last refinement before we try this new code. Now that we have a route for the redirect URI, we can remove the hard-coded constant in auth_helper.rb, and instead use the Rails name for the route: authorize_url.

Aktualisierter Inhalt der Datei .\o365-tutorial\app\helpers\auth_helper.rbUpdated contents of the .\o365-tutorial\app\helpers\auth_helper.rb file

module AuthHelper

  # App's client ID. Register the app in Application Registration Portal to get this value.
  CLIENT_ID = '<YOUR APP ID HERE>'
  # App's client secret. Register the app in Application Registration Portal to get this value.
  CLIENT_SECRET = '<YOUR APP PASSWORD HERE>'

  # Scopes required by the app
  SCOPES = [ 'openid',
             'profile',
             'User.Read',
             'Mail.Read' ]

  # Generates the login URL for the app.
  def get_login_url
    client = OAuth2::Client.new(CLIENT_ID,
                                CLIENT_SECRET,
                                :site => "https://login.microsoftonline.com",
                                :authorize_url => "/common/oauth2/v2.0/authorize",
                                :token_url => "/common/oauth2/v2.0/token")

    login_url = client.auth_code.authorize_url(:redirect_uri => authorize_url, :scope => SCOPES.join(' '))
  end
end

Aktualisieren Sie Ihren Browser (oder wiederholen Sie den Anmeldevorgang). Anstelle einer Rails-Fehlerseite 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.Refresh your browser (or repeat the sign-in process). Now instead of a Rails error page, 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.

Wir fügen eine weitere Hilfsfunktion mit dem Namen get_token_from_code zu auth_helper.rb hinzu.Let's add another helper function to auth_helper.rb called get_token_from_code.

get_token_from_code in der Datei .\o365-tutorial\app\helpers\auth_helper.rbget_token_from_code in the .\o365-tutorial\app\helpers\auth_helper.rb file

# Exchanges an authorization code for a token
def get_token_from_code(auth_code)
  client = OAuth2::Client.new(CLIENT_ID,
                              CLIENT_SECRET,
                              :site => 'https://login.microsoftonline.com',
                              :authorize_url => '/common/oauth2/v2.0/authorize',
                              :token_url => '/common/oauth2/v2.0/token')

  token = client.auth_code.get_token(auth_code,
                                     :redirect_uri => authorize_url,
                                     :scope => SCOPES.join(' '))
end

Nun möchten wir sicherstellen, dass diese funktioniert.Let's make sure that works. Ändern Sie die gettoken-Aktion in der Datei auth_controller.rb so, dass diese Hilfsfunktion verwendet und der Rückgabewert angezeigt wird.Modify the gettoken action in the auth_controller.rb file to use this helper function and display the return value.

Aktualisierter Inhalt der Datei .\o365-tutorial\app\controllers\auth_controller.rbUpdated contents of the .\o365-tutorial\app\controllers\auth_controller.rb file

class AuthController < ApplicationController
  include AuthHelper

  def gettoken
  token = get_token_from_code params[:code]
  render text: "TOKEN: #{token.token}"
  end
end

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

Im Folgenden ändern wir unseren Code so, dass das Token nicht angezeigt, sondern in einem Sitzungscookie gespeichert wird.Now let's change our code to store the token in a session cookie instead of displaying it.

Neue Version der gettoken-AktionNew version of gettoken action

def gettoken
  token = get_token_from_code params[:code]
  session[:azure_token] = token.to_hash
  render text: "Access token saved in session cookie."
end

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 auth_helper.rb hinzu:In order to do that, the app must request the offline_access scope. Add this scope to the SCOPES array in auth_helper.rb:

# Scopes required by the app
SCOPES = [ 'openid',
           'profile',
           'offline_access',
           'User.Read',
           'Mail.Read' ]

Dies bewirkt, dass die Tokenantwort von Azure ein Aktualisierungstoken enthält. Fügen Sie nun eine Hilfsmethode in auth_helper.rb zum Abrufen des zwischengespeicherten Tokens, überprüfen Sie, ob dieses abgelaufen ist, und aktualisieren Sie es, falls erforderlich.This will cause the token response from Azure to include a refresh token. Now let's add a helper method in auth_helper.rb to retrieve the cached token, check if it is expired, and refresh it if so.

get_access_token in der Datei .\o365-tutorial\app\helpers\auth_helper.rbget_access_token in the .\o365-tutorial\app\helpers\auth_helper.rb file

# Gets the current access token
def get_access_token
  # Get the current token hash from session
  token_hash = session[:azure_token]

  client = OAuth2::Client.new(CLIENT_ID,
                              CLIENT_SECRET,
                              :site => 'https://login.microsoftonline.com',
                              :authorize_url => '/common/oauth2/v2.0/authorize',
                              :token_url => '/common/oauth2/v2.0/token')

  token = OAuth2::AccessToken.from_hash(client, token_hash)

  # Check if token is expired, refresh if so
  if token.expired?
    new_token = token.refresh!
    # Save new token
    session[:azure_token] = new_token.to_hash
    access_token = new_token.token
  else
    access_token = token.token
  end
end

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 Microsoft Graph-Clientbibliothek für Ruby herunterzuladen.Let's start by installing the Microsoft Graph Client Library for Ruby. Wir verwenden diesen Gem für alle Outlook-bezogenen Anforderungen.We'll be using this gem for all of our Outlook-related requests.

Öffnen Sie die Datei Gemfile, und fügen Sie diese Zeile an einer beliebigen Stelle in der Datei hinzu:Open up the Gemfile file and add this line anywhere in the file:

gem 'microsoft_graph'

Speichern Sie die Datei, führen Sie bundle install aus, und starten Sie den Server neu.Save the file, run bundle install, and restart the server.

Jetzt erstellen wir einen Controller für E-Mail-Vorgänge.Now let's create a controller for mail operations.

rails generate controller Mail index

Dies weicht etwas von der Vorgehensweise beim Erstellen des Auth-Controllers ab. Diesmal haben wir den Namen einer Aktion übergeben, nämlich index. Rails fügt automatisch eine Route für diese Aktion hinzu und generiert eine Ansichtsvorlage.This is slightly different than how we generated the Auth controller. This time we passed the name of an action, index. Rails automatically adds a route for this action, and generates a view template.

Nun können wir die gettoken-Aktion zum letzten Mal für die Umleitung zu der Indexaktion im E-Mail-Controller ändern.Now we can modify the gettoken action one last time to redirect to the index action in the Mail controller.

Neue Version der gettoken-AktionNew version of gettoken action

def gettoken
  token = get_token_from_code params[:code]
  session[:azure_token] = token.to_hash
  redirect_to mail_index_url
end

Nun werden Sie beim Durchlaufen des Anmeldevorgangs in der App zu der Seite http://localhost:3000/mail/index weitergeleitet. Auf dieser Seite passiert nichts, also sollten wir dies beheben.Now going through the sign-in process in the app lands you at http://localhost:3000/mail/index. Of course that page doesn't do anything, so let's fix that.

Öffnen Sie die Datei .\o365-tutorial\app\controllers\mail_controller.rb, und definieren Sie die index-Aktion:Open the .\o365-tutorial\app\controllers\mail_controller.rb file and define the index action:

Inhalt der Datei .\o365-tutorial\app\controllers\mail_controller.rbContents of the .\o365-tutorial\app\controllers\mail_controller.rb file

class MailController < ApplicationController
  include AuthHelper

  def index
    token = get_access_token

    if token
      # If a token is present in the session, get messages from the inbox
      callback = Proc.new do |r| 
        r.headers['Authorization'] = "Bearer #{token}"
      end

      graph = MicrosoftGraph.new(base_url: 'https://graph.microsoft.com/v1.0',
                                 cached_metadata_file: File.join(MicrosoftGraph::CACHED_METADATA_DIRECTORY, 'metadata_v1.0.xml'),
                                 &callback)

      @messages = graph.me.mail_folders.find('inbox').messages.order_by('receivedDateTime desc')
    else
      # If no token, redirect to the root url so user
      # can sign in.
      redirect_to root_url
    end
  end
end

Zusammenfassung des Codes in der index-Aktion:To summarize the code in the index action:

  • Es wird ein Graph-Client erstellt.It creates a Graph client.
  • Er gibt eine GET-Anforderung an die URL für die Nachrichten im Posteingang mit den folgenden Merkmalen aus:It issues a GET request to the URL for inbox messages, with the following characteristics:
    • Er verwendet die order_by-Methode zum Sortieren der Ergebnisse nach receivedDateTime.It uses the order_by method to sort the results by receivedDateTime.
    • Er legt den Authorization-Header für die Verwendung des Zugriffstokens aus Azure fest.It sets the Authorization header to use the access token from Azure.
  • Er speichert die zurückgegebene Sammlung in der @messages-Variablen. Diese Variable ist in der Ansichtsvorlage verfügbar.It saves the return collection to the @messages variable. This variable will be available to the view template.

Anzeigen der ErgebnisseDisplaying the results

Nun müssen wir die der index-Aktion zugeordnete Ansichtsvorlage für die Verwendung der @messages-Variablen geändert. Öffnen Sie die Datei .\o365-tutorial\app\views\mail\index.html.erb, und ersetzen Sie den Inhalt durch Folgendes:Now we need to modify the view template associated with the index action to use the @messages variable. Open the .\o365-tutorial\app\views\mail\index.html.erb file, and replace its contents with the following:

Inhalt der Datei .\o365-tutorial\app\views\mail\index.html.erbContents of the .\o365-tutorial\app\views\mail\index.html.erb file

<h1>My messages</h1>
<table class="table">
  <tr>
    <th>From</th>
    <th>Subject</th>
    <th>Received</th>
  </tr>
  <% @messages.each do |message| %>
    <tr>
      <td><%= message.from.email_address.name %></td>
      <td><%= message.subject %></td>
      <td><%= message.received_date_time %></td>
    </tr>
  <% end %>
</table>

Die Vorlage ist eine einfache HTML-Tabelle. Sie verwendet eingebetteten Ruby-Code zum Durchlaufen der Ergebnisse in der @messages-Variablen, die wir in der index-Aktion festgelegt haben, und zum Erstellen einer Tabellenzeile für jede Nachricht. Die Syntax für den Zugriff auf die Werte der einzelnen Nachrichten ist einfach.The template is a fairly simple HTML table. It uses embedded Ruby to iterate through the results in the @messages variable we set in the index action and create a table row for each message. The syntax to access the values of each message is straightforward.

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.

Eine HTML-Tabelle mit dem Inhalt eines Posteingangs.

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 auth_helper.rb, um den Calendars.Read-Bereich einzuschließen.Update the SCOPES array in auth_helper.rb to include the Calendars.Read scope.

    # Scopes required by the app
    SCOPES = [ 'openid',
               'profile',
               'offline_access',
               'User.Read',
               'Mail.Read',
               'Calendars.Read' ]
    
  2. Generieren Sie einen Calendar-Controller.Generate a Calendar controller.

    rails generate controller Calendar index
    
  3. Öffnen Sie die Datei .\o365-tutorial\app\controllers\calendar_controller.rb, und fügen Sie den folgenden Code hinzu.Open the .\o365-tutorial\app\controllers\calendar_controller.rb file and add the following code.

    class CalendarController < ApplicationController
    
      include AuthHelper
    
      def index
        token = get_access_token
        if token
          # If a token is present in the session, get events from the calendar
          callback = Proc.new do |r| 
            r.headers['Authorization'] = "Bearer #{token}"
          end
    
          graph = MicrosoftGraph.new(base_url: 'https://graph.microsoft.com/v1.0',
                                    cached_metadata_file: File.join(MicrosoftGraph::CACHED_METADATA_DIRECTORY, 'metadata_v1.0.xml'),
                                    &callback)
    
          @events = graph.me.events.order_by('start/dateTime asc')
        else
          # If no token, redirect to the root url so user
          # can sign in.
          redirect_to root_url
        end
      end
    end
    
  4. Öffnen Sie die Datei .\o365-tutorial\app\views\calendar\index.html.erb, und fügen Sie den folgenden Code hinzu.Open the .\o365-tutorial\app\views\calendar\index.html.erb file and add the following code.

    <h1>My events</h1>
    <table class="table">
      <tr>
        <th>Subject</th>
        <th>Start</th>
        <th>End</th>
      </tr>
      <% @events.each do |event| %>
        <tr>
          <td><%= event.subject %></td>
          <td><%= event.start.date_time %></td>
          <td><%= event.end.date_time %></td>
        </tr>
      <% end %>
    </table>
    

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

  1. Aktualisieren Sie das SCOPES-Array in auth_helper.rb, um den Calendars.Read-Bereich einzuschließen.Update the SCOPES array in auth_helper.rb to include the Calendars.Read scope.

    # Scopes required by the app
    SCOPES = [ 'openid',
               'profile',
               'offline_access',
               'User.Read',
               'Mail.Read',
               'Contacts.Read' ]
    
  2. Generieren Sie einen Contacts-Controller.Generate a Contacts controller.

    rails generate controller Contacts index
    
  3. Öffnen Sie die Datei .\o365-tutorial\app\controllers\contacts_controller.rb, und fügen Sie den folgenden Code hinzu.Open the .\o365-tutorial\app\controllers\contacts_controller.rb file and add the following code.

    class ContactsController < ApplicationController
    
      include AuthHelper
    
      def index
        token = get_access_token
        if token
          # If a token is present in the session, get contacts
          callback = Proc.new do |r| 
            r.headers['Authorization'] = "Bearer #{token}"
          end
    
          graph = MicrosoftGraph.new(base_url: 'https://graph.microsoft.com/v1.0',
                                    cached_metadata_file: File.join(MicrosoftGraph::CACHED_METADATA_DIRECTORY, 'metadata_v1.0.xml'),
                                    &callback)
    
          @contacts = graph.me.contacts.order_by('givenName asc')
        else
          # If no token, redirect to the root url so user
          # can sign in.
          redirect_to root_url
        end
      end
    end
    
  4. Öffnen Sie die Datei .\o365-tutorial\app\views\contacts\index.html.erb, und fügen Sie den folgenden Code hinzu.Open the .\o365-tutorial\app\views\contacts\index.html.erb file and add the following code.

    <h1>My contacts</h1>
    <table class="table">
      <tr>
        <th>First Name</th>
        <th>Last Name</th>
        <th>Email</th>
      </tr>
      <% @contacts.each do |contact| %>
        <tr>
          <td><%= contact.given_name %></td>
          <td><%= contact.surname %></td>
          <td><%= contact.email_addresses[0].address %></td>
        </tr>
      <% end %>
    </table>