REST - Representational State Transfer

Das HTTP ein weit verbreitetes Transportmittel für Informationen ist, weiß mittlerweile auch der normale Anwender durch die Adressliste im Browser. HTTP kann aber auch zwischen Diensten genutzt werden und hier ist die REST - Representational State Transfer eine immer wichtigere Möglichkeit die Daten anzufordern und darzustellen.

Exchange hat selbst On-Premises eine Exchange REST API für Mail, Kontakt, Termine, die aber nicht direkt sondern nur über die Cloud angesprochen werden kann. Siehe dazu Graph und Exchange On-Premises

Der Request

Ein HTTP-Client fordert Daten in der Regel durch einen GET oder POST an, seltener kommt Verben wir PUT, PATCH, DELETE, HEAD, OPTIONS, CONECT oder TRACE zum Einsatz. Über die URL und ggfls. Daten im Body der Anforderung erhält der Server die Information, die dieser zur Beantwortung der Frage benötigt. Im einfachsten Fall ist es einfach die URL selbst, die ausreichend Informationen enthält.

Der wichtige Aspekt hierbei ist, dass der Request alle Informationen enthält und keine Abhängigkeiten von vorherigen Anfragen oder Stati vorhanden sind. Wenn ein Request genau so wiederholt gestellt wird und sich der Datenbestand nicht verändert hat, kommt auch wieder das gleiche Ergebnis zurück.

Eine REST-Schnittstelle ist damit deutlich einfacher als z.B. ein WebService. Bei einem Exchange WebService ist es z.B. so, dass der Client sich erst anmeldet und dann z.B. ein Postfach und einen darin enthaltenen Ordner erreichen muss, ehe dann das einzelne Element abgefragt werden kann.

Die Rückgabe

Der Service verarbeitet die Information und liefert die Daten zurück. Ohne die Beschreibung von REST sind hier sehr viele Optionen möglich und wurden in der Vergangenheit genutzt.

  • Text-Ergebnis
    Es gab Webservices, die einfach einen Text zurück geliefert haben. So liefert z.B. der Exchange Health Check auf einen Anfragen auf https:\\<serverfqdn>\owa\healthcheck.htm einfach nur einen "200OK".
  • Webeites zum Parsen
    Andere Services, die per HTTP erreichbar sind, liefern dennoch nur eine normale "Webseite". Per Skript kann man versuchen, dort die gewünschten Informationen "abzugreifen". Das habe ich z.B. bei PRTG:Kostal so gemacht
  • XML-Antworten
    Es gibt aber auch ohne den "REST"-Begriff schon Webdienste, die als Antwort eine XML-Struktur ausliefern. Das sind neben vielen "WebServices" auch einfachere Systeme wie z. B. der MIBI Temperatur-Sensor,
  • XML/JSON

Eine sehr schöne Einführung habe ich auf YouTube gefunden

REST API concepts and examples
https://www.youtube.com/watch?v=7YcW25PHnAA

REST in Produkten

Aufgrund der Einfachheit von REST für den Client rüsten immer mehr Hersteller ihre Produkte um eine RESTful API nach. Hier ein paar Beispiele

Produktname  Version  Links und Hinweise

Exchange Online

Ab 2015 

Exchange 2016

Seit 2016 CU3

KEMP

 

Lync/Skype für Business

2013

PRTG

 

Dynamics NAW

2013R2

Office 365

Jahr 2015

Facebook
Instagram

 

https://developers.facebook.com
https://developers.facebook.com/products/instagram/

Es gibt noch sehr viele weitere Anbieter und ich denke REST ist die nächste API, die WebServices verdrängen kann.

Einsatz mit PowerShell (PS 3.0 oder höher)

Ab PowerShell 3.0 können Sie sehr viele Aufgaben als HTTP-Client in einer Zeile lösen, z.B.: den Download einer Datei.

$data= Invoke-WebRequest `
   -Uri "http://www.msxfaq.de" `
   -Outfile msxfaq-homepage.htm

Analog dazu können Sie auch Daten per POST an eine Webseite hochladen.

$data= Invoke-WebRequest `
   -Method post `
   -Uri "http://localhost:81/post.txt" `
   -Body "bodtest"

Das macht es natürlich schon sehr viel einfacher mit einem Webserver zu interagieren.

Die Antwort im Erfolgsfalle ist ein HtmlWebResponseObject.

Interessant sind hier bei die beiden Properties "StatusCode" und "StatusDescription". Der Inhalt ist in dem Property "Content". Wer dem kompletten HTML-Inhalt samt Header benötigt, nutzt "RawContent". Interessanter sind aber vor allem die vorgearbeiteten Properties wie z.B."Links", welches alle Hyperlinks ausgibt.

Hinweis
Die Rückgabe enthält nur dann ein Objekt, wenn die Abfrage erfolgreich war. Fehler werden immer mit einer "Exception" abgefangen. Erwarten Sie also nie in $data.Statuscode einen 401 o.ä. 

Achtung: Invoke-Webrequest nutzt per Default DOM des IE. Wenn dieser nicht installiert oder limitiert ist, kann das Parsen nicht funktionieren. Nutzen Sie dann den Parameter "-UseBasicParsing".
Dummerweise werden dann wohl nur Links, Forms, Images und Header auseinander genommen aber das Feld "ParsedHTML" bleibt leer.

Achtung: Beide Commandlets senden nicht zwingend den kompletten Request in einem Paket. Es ist durchaus erlaubt den Request auf mehrere Pakete aufzuteilen und dem Ziel über das Feld "Content-Length" mitzuteilen, wie groß die Daten sind. Das Ziel muss dann mit einem "100 Continue" eine Zwischenbestätigung senden. Leider "verstehen" das gerade kleine Geräte das nicht immer, wie ich auf PRTG Edimax SP2101W z.B. herausgefunden habe.

REST und Authentifizierung

Rest ist einfach und auch wenn der Unterbau wieder HTTP ist, so sind nicht alle REST-Services vollständig. Ich habe das bei der Abfrage der RestAPI von MobileIron gemerkt, von der ich die Liste der Geräte erhalten wollte. Mit "Invoke-RESTMethod" verbindet sich die PowerShell erst einmal anonym und erwartet dann, dass der Webserver nicht nur einen 401 sendet, sondern auch die möglichen Anmeldeverfahren mitteilt. Wenn dies aber unterbleibt, weiß der Client nicht wie er weiter machen soll. So etwas können Sie recht einfach per Fiddler sehen. Hier ein Beispiel.

Sie sehen oben den Request und unten die Antwort mit dem 401 aber ohne Hinweise auf die verfügbaren Authentifizierungsmethoden. In dem Fall können sie nur "Raten" und die Anmeldedaten auf Verdacht mit senden. Da dies mit Invoke-RESTMethod nicht über den Parameter "-Credentials" möglich ist, müssen Sie die Authentifizierung im Header selbst unterbringen. Das ist aber nicht sonderlich schwer, wenn man es mit "BASIC-Authentication" zu tun hat. Hier ein Beispiel:

$User = "Username"
$pass = "PassWord1+"

$basicauth=[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($User+":"+$pass))
$headers=@{}
$headers.Add("Authorization","Basic $basicauth")
$headers.Add("ContentType","application/json")

$result = Invoke-RestMethod -URI ('https://mdmserver/api/v2/ping') -headers $headers

Sie sollten natürlich das Kennwort nicht im Code verstecken und bei der Nutzung von "Basic" sollten Sie auf ein HTTPS achten. Das Kennwort kann sonst sehr einfach abgefischt werden.

Weitere Links