Mails senden mit der Microsoft Graph API

Im Beitrag „Outlook.com mit Access und VBA: Vorbereitungen“ haben wir gezeigt, wie wir die Voraussetzungen für den Zugriff auf unser Outlook-Konto mit der Rest-API von Microsoft erschaffen. Dabei haben wir eine Anwendung bei Microsoft registriert und die notwendigen Daten wie die Application-ID, die Tenant-ID und die SecretID ermittelt, die wir für den Zugriff auf die Rest-API benötigen. Außerdem haben wir dort die benötigten Berechtigungen eingestellt, hier erst einmal für das Senden von E-Mails. Was wir nun benötigen, sind zwei Dinge: Erstens ein Token, das wir für die Authentifizierung bei der Rest-API benötigen, zweitens den VBA-Code für die Ausführung der eigentlichen Rest-API-Aufrufe. Damit erstellen wir eine erste Prozedur, mit der wir E-Mails über unser Outlook.com-Konto versenden können. Darauf aufbauend werden wir in weiteren Beiträgen zeigen, wie wir weitere Funktionen implementieren können.

Die Ergebnisse des Beitrags Outlook.com mit Access und VBA: Vorbereitungen (www.access-im-unternehmen.de/1531) sehen wie folgt aus:

  • Wir haben eine neue App registriert.
  • Wir haben für die Anwendung die Anwendungs-ID (ApplicationID), die Verzeichnis-ID (TenantID) und den geheimen Client-Schlüssel (SecretID) ermittelt und in VBA-Konstanten gespeichert.
  • Wir haben die Berechtigungen für die neue App so eingestellt, dass wir damit E-Mails versenden können (Mail.Send).

Damit steigen wir nun in die Programmierung des Zugriffs auf die Microsoft Graph API für den Zugriff auf unser Outlook-Konto ein.

Dazu führen wir folgende Schritte durch:

  • Wir fügen die notwendigen Verweise hinzu.
  • Wir fügen zwei Module hinzu, die Funktionen für den Umgang mit JSON-Dokumenten liefern.
  • Wir erstellen eine Basisfunktion zum Durchführen von Rest-API-Aufrufen.
  • Wir erstellen eine Funktion, mit der wir den Authentifizierungstoken für den Zugriff auf die Rest-API holen können.
  • Wir erstellen eine Funktion, mit der wir eine E-Mail versenden können.

Verweise hinzufügen

Wir benötigen die folgenden beiden Verweise:

  • Microsoft Scripting Runtime
  • Microsoft XML, v6.0

Der Verweise-Dialog sieht nach dem Hinzufügen dieser Verweise wie in Bild 1 aus.

Verweise, die wir für den Zugriff auf die Rest-API benötigen

Bild 1: Verweise, die wir für den Zugriff auf die Rest-API benötigen

Module hinzufügen

Wenn Sie die Beispieldatenbank neu aufbauen wollen, fügen Sie nun aus unserer Datenbank die beiden Module mdlJSON und mdlJSONDOM hinzu.

Das erste enthält Methoden, um JSON-Dokumente in ein Objektmodell zu überführen und um JSON-Dokumente wieder aus diesem Objektmodell auszulesen.

Das zweite bietet die Möglichkeit, die Anweisungen auszugeben, die für den Zugriff aller Elemente dieses Objektmodells nötig sind. Die genaue Funktionsweise zeigen wir gleich im Praxiseinsatz.

Token für die Authentifizierung holen

Es gibt verschieden aufwendige Zugriffsarten für Rest-APIs.

Manche begnügen sich damit, dass man ein API-Kennwort auf der Webseite des Anbieters holt und dieses in seinen Aufruf einbettet.

Andere erfordern es, dass man mit den Daten wie der Anwendungs-ID und einem geheimen Schlüssel erst noch ein Authentifizierungstoken holt, mit dem man dann den eigentlichen Aufruf durchführen kann. Dies ist hier der Fall, da wir im Kontext der Anwendung auf die Methoden wie Mail.Send zugreifen.

Einen Schritt weiter gehen wir, wenn wir eine Anwendung für den Zugriff auf eine Rest-API bauen, die im Kontext eines Benutzers verwendet werden soll. Dann müssten wir zum Holen des Authentifizierungstokens erst noch eine Webseite anzeigen, auf der sich der Benutzer authentifiziert, bevor wir das Token erhalten. Auch dies ist bei der Rest-API möglich, die wir hier ansteuern wollen.

Für unsere Zwecke reicht allerdings die Methode aus, mit der wir auch das Token über die Rest-API holen.

Das erledigen wir mit der Funktion aus Listing 1. Die Funktion hat zwei Parameter:

Function GetAccessToken(strToken As String, strResponse As String) As Boolean
     Dim objXMLHTTP As MSXML2.XMLHTTP60
     Dim objJSON As Object
     Dim strURL As String
     Dim strData As String
     Dim strJSON As String
     
     strURL = "https://login.microsoftonline.com/" & cStrTenantID & "/oauth2/v2.0/token"
     
     strData = "grant_type=client_credentials" & _
            "&client_id=1" & cStrApplicationID & _
            "&client_secret=" & cStrSecretValue & _
            "&scope=https://graph.microsoft.com/.default"
     
     Set objXMLHTTP = CreateObject("MSXML2.XMLHTTP")
     objXMLHTTP.Open "POST", strURL, False
     objXMLHTTP.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
     objXMLHTTP.Send strData
     
     Select Case objXMLHTTP.status
         Case 200
             strJSON = objXMLHTTP.responseText
             Set objJSON = ParseJson(strJSON)
             GetAccessToken = True
             strToken = objJSON("access_token")
         Case Else
             strJSON = objXMLHTTP.responseText
             Set objJSON = ParseJson(strJSON)
             strResponse = objJSON.Item("error_description")
             Debug.Print GetJSONDOM(strJSON, True)
     End Select
End Function

Listing 1: Funktion zum Holen des Access-Tokens

  • strToken: Wird bei erfolgreichem Aufruf mit dem Token gefüllt.
  • strResponse: Wird im Fehlerfall mit der Fehlermeldung gefüllt.

Das Ergebnis der Funktion selbst ist ein Boolean-Wert. Ist dieser True, war der Abruf des Tokens erfolgreich, anderenfalls liefert die Funktion den Wert False zurück.

Die Funktion deklariert zunächst eine Objektvariable des Typs XMLHTTP60. Diese ermöglicht den Zugriff auf die Rest-API. In objJSON speichern wir das Objektmodell der Antwort im JSON-Format. strURL nimmt die Adresse des Endpunktes der Rest-API auf und strData die zu übergebenden Parameter. In strJSON speichern wir schließlich das Ergebnis des Aufrufs.

Die Funktion trägt als Erstes die URL für den Endpunkt der Rest-API in die Variable strURL ein. Diese besteht aus der Zeichenkette https://login.microsoftonline.com/, der Tenant-ID sowie der Zeichenkette /oauth2/v2.0/token.

Insgesamt sieht die URL beispielsweise wie folgt aus:

https://login.microsoftonline.com/dcc87x1c-44xb-43x8-9x0d-xa1cxxxxxxxx/oauth2/v2.0/token

Danach folgt die Variable strData, in der wir die übrigen für die Authentifizierung benötigten Informationen einarbeiten.

Der darin gespeicherte Ausdruck sieht anschließend wie folgt aus:

grant_type=client_credentials&client_id=15bddxdx5-xbf3-x248-bxaa-fd41x6xb18xd&client_secret=6Ax8Q~ZxDxItTxAlycqxPcY6kxZnpfxzitTxca2e&scope=https://graph.microsoft.com/.default

Damit erstellen wir nun eine Instanz von MSXML2.XMLHTTP und referenzieren diese mit der Variablen objXMLHTTP.

Wir rufen die Open-Methode dieses Objekts auf und übergeben drei Parameter:

  • bstrMethod: Methode des Zugriffs, in diesem Fall POST
  • bstrURL: URL, hier aus der Variablen strURL
  • varAsync: Gibt an, ob der Aufruf synchron oder asynchron erfolgen soll, hier False für einen synchronen Aufruf.

Mit der setRequestHeader-Eigenschaft legen wir den Wert für das Attribut Content-Type fest, hier auf application/x-www-form-urlencoded.

Schließlich senden wir die Anfrage mit der Send-Methode und dem Parameter strData ab.

Durch die Angabe, dass wir einen synchronen Aufruf starten wollen, wird der Code erst fortgesetzt, wenn der Aufruf beendet ist und das XMLHTTP-Objekt das Ergebnis enthält.

Wir können dann anhand der Eigenschaft Status ermitteln, ob der Aufruf erfolgreich war oder ob ein Fehler aufgetreten ist. Liefert Status den Wert 200 zurück, war der Aufruf erfolgreich.

In diesem Fall erhalten wir mit objXMLHTTP.responseText die folgende Antwort (hier in gekürzter Form), die wir anschließend in die Variable strJSON schreiben:

{"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":"eyJ0e...5_7xA"}

Wir erstellen daraus mit der Funktion ParseJson ein Objektmodell, das wir mit der Variablen objJSON referenzieren und stellen als Rückgabewert der Funktion den Wert True ein.

Außerdem tragen wir für den Rückgabeparameter strToken das Ergebnis des Ausdrucks objJSON(„access_token“) ein.

Woher wissen wir, dass wir diesen Ausdruck aufrufen müssen, um das Token auszulesen?

Dazu können wir die folgende Zeile zwischenschalten:

Debug.Print GetJSONDOM(strJSON, True)

Dies liefert das folgende Ergebnis im Direktbereich des VBA-Editors:

objJSON.Item("token_type"):Bearer
objJSON.Item("expires_in"):3599
objJSON.Item("ext_expires_in"):3599
objJSON.Item("access_token"):eyJ0eXAiOi...jmubh-6KQ

GetJSONDOM hat die Aufgabe, den Inhalt des Objektmodells aus objJSON dahingehend zu analysieren, dass es die Aufrufe ausgibt, die nötig sind, um die entsprechenden Inhalte abzurufen. Durch das Einstellen des zweiten Parameters bolValues auf den Wert True werden wie oben angezeigt auch die Werte ausgegeben.

Wenn wir nun gezielt den Wert des Tokens auslesen wollen, müssen wir also auf objJSON.Item(„access_token“) zugreifen. Wichtig ist, dass wir zuvor mit Set objJSON = ParseJSON(strJSON) das Objektmodell erzeugt haben.

Wenn Status einen anderen Wert als 200 zurückgibt, beispielsweise 400 oder 401, ist offensichtlich ein Problem aufgetreten. In diesem Fall untersuchen wir die Antwort ebenfalls und schreiben den Wert aus objJSON.Item(„error_description“) als Rückgabewert in die Variable strResponse.

Zum Testen dieser Funktion können wir die folgende Prozedur nutzen:

Public Sub Test_GetAccessToken()
     Dim strToken As String
     Dim strResponse As String
     If GetAccessToken(strToken, strResponse) = True Then
         Debug.Print "Token erfolgreich geholt:"
         Debug.Print strToken
     Else
         Debug.Print "Token nicht geholt:"
         Debug.Print strResponse
     End If
End Sub

Diese ruft die Funktion GetAccessToken mit den Parametern strToken und strResponse auf, die jeweils leer sind, und gibt die Ergebnisse aus.

E-Mail via Microsoft Graph API versenden

Damit können wir uns der eigentlichen Aufgabe widmen: Einer Prozedur, mit der wir per VBA eine E-Mail versenden und dabei die Microsoft Graph API nutzen.

Diese Prozedur finden wir in Listing 2.

Public Sub SendEmail()
     Dim objXMLHTTP As MSXML2.XMLHTTP60
     Dim strUser As String
     Dim strURL As String
     Dim strToken As String
     Dim strBody As String
     Dim strResponse As String
     
     If GetAccessToken(strToken, strResponse) = True Then
     
         ''strURL = "https://graph.microsoft.com/v1.0/me/sendMail"
         strUser = "andre@andreminhorstverlag.onmicrosoft.com"
         strURL = "https://graph.microsoft.com/v1.0/users/" & strUser & "/sendMail"
         
         strBody = strBody & "{" & vbCrLf
         strBody = strBody & "  ""message"": {" & vbCrLf
         strBody = strBody & "    ""subject"": ""Test E-Mail""," & vbCrLf
         strBody = strBody & "    ""body"": {" & vbCrLf
         strBody = strBody & "      ""contentType"": ""Text""," & vbCrLf
         strBody = strBody & "      ""content"": ""Dies ist eine Test-E-Mail über Microsoft Graph.""" & vbCrLf
         strBody = strBody & "    }," & vbCrLf
         strBody = strBody & "    ""toRecipients"": [" & vbCrLf
         strBody = strBody & "      {" & vbCrLf
         strBody = strBody & "        ""emailAddress"": {" & vbCrLf
         strBody = strBody & "          ""address"": ""andre@minhorst.com""" & vbCrLf
         strBody = strBody & "        }" & vbCrLf
         strBody = strBody & "      }" & vbCrLf
         strBody = strBody & "    ]" & vbCrLf
         strBody = strBody & "  }," & vbCrLf
         strBody = strBody & "  ""saveToSentItems"": ""true""" & vbCrLf
         strBody = strBody & "}" & vbCrLf
         
         Set objXMLHTTP = CreateObject("MSXML2.XMLHTTP")
         objXMLHTTP.Open "POST", strURL, False
         objXMLHTTP.setRequestHeader "Authorization", "Bearer " & strToken
         objXMLHTTP.setRequestHeader "Content-Type", "application/json"
         objXMLHTTP.Send strBody
         
         Select Case objXMLHTTP.status
             Case 202
                 MsgBox "E-Mail erfolgreich gesendet."
             Case Else
                 MsgBox "Fehler beim Senden der E-Mail: " & objXMLHTTP.status & " - " & objXMLHTTP.responseText
         End Select
     End If
End Sub

Listing 2: Prozedur zum Senden einer E-Mail

Die Prozedur deklariert wieder ein XMLHTTP60-Objekt sowie einige String-Variablen.

Sie ruft die Funktion GetAccessToken auf, um das Authentifizierungstoken zu holen. Gelingt dies, trägt sie die E-Mail-Adresse, die als Absenderadresse verwendet werden soll, in die Variable strUser ein.

Wir verwenden hier die Adresse, unter der auch das Microsoftkonto betrieben wird beziehungsweise die Microsoft für uns angelegt hat.

Diese Adresse fügen wir in die URL ein, die wir der Variablen strURL hinzufügen und die beispielsweise wie folgt aussieht:

https://graph.microsoft.com/v1.0/users/andre@andreminhorstverlag.onmicrosoft.com/sendMail

Dann stellen wir in der Variablen strBody die Eigenschaften und Inhalte der E-Mail zusammen. Der Inhalt sieht in diesem Fall danach wie folgt aus:

{
   "message": {
     "subject": "Test E-Mail",
     "body": {
       "contentType": "Text",
       "content": "Dies ist eine Test-E-Mail über Microsoft Graph."
     },
     "toRecipients": [
       {
         "emailAddress": {
           "address": "andre@minhorst.com"
         }
       }
     ]
   },
   "saveToSentItems": "true"
}

Hier sehen wir bereits die wichtigsten Elemente einer E-Mail:

  • subject: Betreff der E-Mail
  • body: Inhalt der E-Mail mit einigen Unterelementen
  • contentType: Format des Inhalts, hier Text. Alternativ: HTML.
  • content: Eigentlicher Inhalt der E-Mail
  • toRecipients: Auflistung aller To-Empfänger mit den Unterelementen emailAddress und address.
  • saveToSentItems: Gibt an, ob die E-Mail in den gesendeten Objekten gespeichert werden soll.

Danach erstellt die Prozedur das XMLHTTP-Objekt und referenziert es mit der Variablen objXMLHTTP. Es öffnet das Objekt mit der Open-Methode und übergibt die bereits bekannten Parameter.

Sie fügt jedoch einen zusätzlichen RequestHeader namens Authorization hinzu, der die Zeichenkette Bearer und das zuvor ermittelte Token enthält.

Schließlich wird die Anfrage mit der Send-Methode und dem Inhalt aus strBody als Parameter abgesendet.

Das Ergebnis werten wir wieder in einer Select Case-Bedingung aus. Diesmal erhalten wir für die erfolgreiche Ausführung jedoch den Statuswert 202 zurück, was wir entsprechend berücksichtigen. Im Fehlerfall geben wir den Fehler als Meldung aus.

Hier ist der Versand jedoch erfolgreich und wir finden die E-Mail im Postfach des Empfängers wieder (siehe Bild 2).

Die erfolgreich gesendete E-Mail im neuen Outlook

Bild 2: Die erfolgreich gesendete E-Mail im neuen Outlook

Nächste Schritte

Nachdem wir die erste E-Mail erfolgreich versendet haben, können wir die nächsten Schritte angehen:

Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...

den kompletten Artikel im PDF-Format mit Beispieldatenbank

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar