Die Benutzung von REST-basierten Services ist eine sehr schöne Möglichkeit
andere Systeme und Anwendungen mit dem Lotus Domino Server zu integrieren.
Dieser Blogeintrag fasst die Bedeutung
und Vorzüge von REST im Allgemeinen zusammen und zeigt die Grundlagen der
Benutzung der REST Controls innerhalb der XPages Extension Library.
REST-basierte (oder RESTful) Webservices
sind sowohl für Integrationsszenarios nützlich als auch zur Vorhaltung
von Daten für externe Anwendungen.
Ich benutzte die REST Services zum Beispiel
kürzlich, um eine Schnittstelle für eine mobile Anwendung zu realisieren.
RESTful Webservices (Falls Sie sie noch nicht kannten..)
Representational State Transfer (REST)
bezeichnet einen Software-Architekturstil, der von Roy Thomas Fielding
in seiner Dissertation
beschrieben wurde und der Architektur des Internets entspricht. Genauer
gesagt ist das im Internet und durch RESTful Webservices verwendete HTTP-Protokoll
eine Implementierung des REST-Architekturstils.
Wenn Sie also die Prinzipien des HTTP-Protokolls
kennen, wissen Sie auch schon viel über REST. Um die wichtigsten Punkte
zu nennen:
- Informationen werden mittels sogenannter Ressourcen zu Verfügung gestellt. Jede Ressource kann eindeutig über eine ID angesprochen werden (die URL).
- "Respresentational" meint das Übertragen der Informationen in verschiedenen Repräsentationen, also Formaten. Das heißt sowohl im technischen Sinne (z.B. JSON, XML) als auch im inhaltlichen Sinne (z.B. Text, Bilder).
- Zustandslosigkeit. Der Server behält keinen (Session-) Status über eine Folge von Requests.
- Ressourcen können Verknüpfungen zu anderen Ressourcen enthalten, wodurch eine Navigation ermöglicht wird.
- Die Operationen, die auf einer Ressource
ausgeführt werden können, sind generischer Form. Typischerweise werden
folgende vier verwendet:
- GET - lesender Zugriff auf eine Ressource, frei von Seiteneffekten
- POST - Erstellen einer neuen Ressource
- PUT - Erstellen oder Aktualisieren einer bestimmten Ressource (bestimmte ID)
- DELETE - Löschen einer Ressource
RESTful Webservices stellen einen auf
HTTP basierenden Service bereit, der den genannten Prinzipien folgt.
Einige generelle Vorteile von RESTful
Webservices sind:
- Loose Kopplung von Anwendungen und Systemen (führt zu hoher Interoperabilität)
- Einfaches Ressourcen-orientiertes Konzept mit wenigen generischen Methoden
- Gute Skalierbarkeit
- Einfaches Caching
Im Wesentlichen sind dies auch die Vorzüge
des HTTP-Protokolls.
REST-basierte Services zum Zugriff auf Domino-Daten
Eine gute Übersicht über die REST-Funktionen
in der Extension Library bietet das Video "REST
Services for Domino and XPages" auf der Extension Library
Homepage.
An dieser Stelle werde ich insbesondere
die Benutzung der XPages Controls für REST vorstellen. Es gibt noch zwei
alternative Wege, die außerhalb des XPages-Kontexts funktionieren (Benutzung
des Domino Data Service und Erstellen eines Custom Servlets).
Es gibt verschiedene XPages Controls,
die man zum Zugriff auf Domino-Daten benutzen kann. Zum Beispiel:
- Database Collection Service (xe:databaseCollectionJsonService): Abrufen einer Datenbankliste auf dem Server.
- View Collection Service (xe:viewCollectionJsonService): Abrufen einer Liste von Ansichten und Ordnern in einer Datenbank.
- View Service (xe:viewJsonService): Lesen von Ansichts- und Ordnerdaten (mit Filterung), Erstellen, Aktualisieren und Löschen von Dokumenten (begrenzt).
- Document Service (xe:documentJsonService): Operation auf Dokumenten.
Wie Sie vielleicht erahnen, steht das
"Json" innerhalb der Tags der Controls für das verwendete Übertragungsformat
JSON.
Beispiele der Controls können auch in
der Beispieldatenbank innerhalb des Extension Library
Downloads gefunden werden.
Für die XPages REST Controls ist es
nicht erforderlich etwas auf dem Domino Server zu konfigurieren (aber es
bringt eine kleine Vereinfachung, wie wir später sehen werden).
Es genügt eine XPage zu erstellen und
die gewünschten REST Controls hinzuzufügen, um Zugriff auf die gewünschten
Daten zu ermöglichen.
Um dies detaillierter zu zeigen und
die wichtigsten Parameter zu erklären, werden wir ein einfaches Beispielszenario
mit einer Maske "company" und einer Ansicht "companiesByName"
verwenden.
Die Services, die wir benötigen, um
in bequemer Weise mit den Firmendokumenten zu arbeiten sind:
- xe:viewJsonService (Auflistung bestehender Firmen)
- xe:documentJsonService (Erstellen, Lesen, Aktualisieren, Löschen bestimmter Firmendokumente)
Es ließe sich auch der View JSON Service
zum Ändern von Dokumenten benutzen. In diesem Fall ist man jedoch auf die
Felder im Dokument beschränkt, die direkt einer Ansichtsspalte zugeordnet
sind.
Mit dem Document JSON Service lassen
sich sogar Rich Text Felder und Anhänge lesen und bearbeiten.
Zugriff auf die Ansichtsdaten
Wir starten mit einer leeren XPage und
dem Hinzufügen eines REST Service Controls (siehe Screenshot)
Einige Eigenschaften dieses Controls:
- ignoreRequestParams: Parameter in der URL werden für den REST Service ignoriert (analog zu der gleichnamigen Eigenschaft der XPages Datenquellen)
- pathInfo: Dieser String identifiziert den Service auf der XPage. Für den View JSON Service in diesem Beispiel ist diese Eigenschaft "companies".
- service: Hier wird der konkrete REST Service angegeben, einer der Services aus der Liste (siehe Screenshot). Im Beipiel der "xe:viewJsonService".
Die Eigenschaften des View JSON Service
sind über die hinterlegten Beschreibungen schon fast selbsterklärend. Trotzdem
hier ein paar wichtige:
- columns: Für diese Eigenschaft werden Elemente vom Typ "xe:restViewColumn" angegeben, welche wiederum eine Eigenschaft "columnName" (Referenz zur Ansichtsspalte) und "name" (Eigenschaftsname des JSON Objekts in der Ausgabe). Letzteres Mapping wird nur bei GET Requests angewendet. Außerdem gibt es hier noch eine Eigenschaft "value", die zur Berechnung eines Wertes benutzt werden kann.
- defaultColumns: Wenn "true", werden alle Spalten der Ansicht in die Ausgabe einbezogen.
- systemColumns: Spezielle System-Spalten, die nicht notwendigerweise als benutzerdefinierte Spalten angegeben sein müssen (z.B. UNID, Maskenname, Anwortdokument (ja/nein)).
- viewName: Name der Ansicht, die für diesen REST Service benutzt werden soll.
Außerdem gibt es einige Eigenschaften
zur Filterung der zurückgegebenen Ergebnisliste, wie z.B. "start"
und "count" zur Realisierung von Paging, oder "search"
zur Volltextsuche.
Auch Methoden zur Reaktion auf Dokumentenereignisse,
wie "querysaveDocument" stehen zur Verfügung. Diese werden weiter
unten erklärt.
Der folgende Screenshot zeigt die View
Service Eigenschaften, die in diesem Beispiel benutzt wurden:
Testen der API
Um auf den Service zuzugreifen, muss
der für die Eigenschaft "pathInfo" angegebene String verwendet
werden. Das generelle URL Schema im XPages Kontext ist:
http://{Host}/{Datenbank}/{XPage}/{pathInfo}/unid/{unid}?{Parameter}
Für den View Service in diesem Beipiel
ist es nicht erfoderlich auf einzelne Dokumente zuzugreifen, daher kann
der UNID-Teil weggelassen werden.
Angenommen, die Datenbank heißt "Test1.nsf"
und die XPage "data.xsp", so wäre die URL:
http://localhost/Test1.nsf/dat...
Die URL kann direkt über den Browser
aufgerufen werden (impliziert Request-Methode GET) und die Einträge in
der Ansicht sollten im JSON Format angezeigt werden.
Für diesen Zweck und zum weiteren schnellen
Testen der API gibt es ein schönes Firefox Add-on mit dem Namen RESTClient. Damit
lassen sich benutzerdefinierte Requests erstellen durch Angabe von HTTP-Methode,
URL, Request Header und Request Body. Nach dem Senden der Anfrage wird
die Antwort vom Server angezeigt.
Der folgende Screenshot zeigt die Antwort
für den View Service "companies" im RESTClient:
In der Antwort sind JSON-Eigenschaften
mit einem '@' am Anfang enthalten. Diese markieren Systemspalten (nicht-benutzerdefinierte
Spalten).
Die verschiedenen Datentypen werden
in verschiedenen Formaten dargestellt, z.B. werden Strings in Anführungszeichen
eingeschlossen, Zahlen nicht.
Eine detaillierte Beschreibung über
diese und weitere Formate ist in der Dokumentation zu finden (Datei "DominoDataServiceDoc.zip"
im Extension
Library Download).
Arbeiten mit Dokumenten
Um ein API für einzelne Dokumente zur
Verfügung zu stellen, muss ein zweites REST Service Control zu der bestehenden
XPage hinzugefügt werden. Diesmal wird die "pathInfo"-Eigenschaft
entsprechend dem Zugriff auf ein einzelnes Firmendokument benannt, z.B.
"company". Der konkrete Service ist der Document Service (xe:documentJsonService):
Die folgenden Eigenschaften sind gesetzt:
- compact: Bei "true" enthält die Ausgabe keine nicht sichtbaren Zeichen.
- defaultItems: Bei "true" werden alle Items sowie einige Metainformationen in die Ausgabe aufgenommen.
- formName: Name der Maske, die zum Erstellen von Dokumenten verwendet werden soll.
Um den neuen Service zu Testen lässt
sich wieder der RESTClient oder direkt der Browser verwenden (die UNID
kann aus der View Service Antwort kopiert werden):
http://localhost/Test1.nsf/dat...
Das Ergebnis sollte nun alle Felder
des Dokuments enthalten, sowie einige Metainformationen (z.B. @unid, @form,
$UpdatedBy).
Der nächste Schritt besteht im Aktualisieren
eines bestehenden Dokuments. Aktualisieren wird für gewöhnlich über die
HTTP-Methode PUT durchgeführt. Die Extension Library Implementierung macht
hier noch eine weitere Unterscheidung: Die PUT-Methode wird zum Ersetzen
des bestehenden Dokumenteninhalts mit den Daten im Request verwendet, wobei
vorheriger Inhalt verworfen wird. Das Dokument wird anschließend genau
die Daten enthalten, die im Request gesendet wurden.
Aber in den meisten Fällen wird man
vermutlich nur einige Items ändern wollen und den Rest des Inhalts unberührt
lassen. Dies wird in der Extension Library über eine weitere HTTP-Methode
"PATCH" realisiert.
An diesem Punkt ist es erwähnenswert,
dass der Domino Server in der Voreinstellung nur die Mehtoden GET und POST
unterstützt. Die weiteren Methoden zu erlauben ist eine Einstellungssache
in einem Internet Site Dokument, allerdings gibt es noch einen alternativen
Ansatz, der auch dann sehr nützlich ist, wenn auf Client-Seite keine PATCH
Requests unterstützt werden:
Die HTTP-Methoden PUT, PATCH und DELETE
können alternativ auch durch die POST-Methode ersetzt werden und ein zusätzlicher
HTTP Header "X-HTTP-Method-Override" kann gesetzt werden, der
dann die zu verwendende Methode spezifiziert.
Wie lässt sich also das Ändern eines
Items in einem bestimmten Dokument mit Hilfe des RESTClient testen?
1. Setzen des "Method-Override"
Header
Im RESTClient von der Titelleiste folgenden
Punkt wählen: Headers -> Custom Header
Der folgende Dialog sollte wie folgt
befüllt werden:
2. Es gibt noch einen weiteren Header,
der gesetzt werden muss. Ansonsten wird die Anfrage mit einem Fehler quittiert:
Es geht um den "Content Type"
der Anfrage, der das JSON-Format angeben muss.
Header Name: Content-Type
Header Wert: application/json
3. Ein bestehendes Dokument per GET
abrufen (nur um Daten zum Ändern zu haben).
4. Die Request-Methode auf POST ändern
und die korrekte Angabe der Header prüfen (siehe auch folgender Screenshot).
5. Im Request Body die zu ändernden
Daten angeben (im JSON Format).
6. Per "Send" die Anfrage
abschicken. Wenn es keine Fehlermeldung gibt, kann das Ergebnis mit einem
erneutem GET überprüft werden.
Andere Operationen:
- Erstellen eines neuen Dokuments: Setzen der HTTP-Methode auf POST (ohne X-HTTP-Method-Override Header)
- Löschen eines Dokuments: Setzen der HTTP-Methode auf DELETE (oder Methode POST und X-HTTP-Method-Override Header auf DELETE)
Programmatisch die Funktion des Document Service erweitern
Wenn Sie einen Ansatzpunkt zur programmatischen
Erweiterung der Request-Verarbeitung benötigen, besteht eine Möglichkeit
darin die Dokumentenereignisse, wie "querySaveDocument" zu verwenden.
Auf diesem Weg können zusätzliche Aufgaben
durchgeführt werden und Dokumenteninhalt geändert oder ergänzt werden.
Die folgende Liste bietet eine Übersicht
über die Parameter der verschiedenen Methoden:
- queryNewDocument: Keine Parameter
- queryOpenDocument: Parameter 'id' (Typ String)
- querySaveDocument: Parameter 'document' (Typ lotus.domino.Document)
- queryDeleteDocument: Paramter 'id' (Typ String)
- postNewDocument: Parameter 'document' (Typ lotus.domino.Document)
- postOpenDocument: Parameter 'document' (Typ lotus.domino.Document)
- postSaveDocument: Parameter 'document' (Typ lotus.domino.Document)
- postDeleteDocument: Paramter 'id' (Typ String)
Wenn die Request-Verarbeitung abgebrochen
werden soll, bieten alle Methoden, deren Name mit "query" beginnt,
einen booleschen Rückgabewert. Wird 'false' zurückgegeben, so wird eine
Exception geworfen und eine entsprechende Fehlernachricht wird zum Client
gesendet. Wenn 'true' zurückgegeben wird, so wird die Verarbeitung fortgesetzt.
Für noch mehr Kontrolle über die Verarbeitung
und die generierte Ausgabe gibt es außerdem die Möglichkeit, ein Custom
Servlet zu schreiben. Wie das funktioniert wird Thema eines zukünftigen
Blogeintrags sein.