Konzept REST

IFS-Logo Diese Seite ist ein Teil der IsyFact-Standards. Alle Inhalte der Seite, insbesondere Texte und Grafiken, sind urheberrechtlich geschützt. Alle urheberrechtlichen Nutzungs- und Verwertungsrechte liegen beim Bundesverwaltungsamt.

Creative Commons Namensnennung Die Nutzung ist unter den Lizenzbedingungen der Creative Commons Namensnennung 4.0 International gestattet.

1. Einleitung

Dieses Dokument enthält das Konzept zum Erstellen von REST-Schnittstellen. Es sorgt für eine einheitliche Umsetzung der Prinzipien in allen IT-Systemen. Es richtet sich vorwiegend an Architekten und Entscheidungsträger.

Dafür beschreibt es in Eigenschaften REST-Schnittstellen zuerst die allgemeinen Eigenschaften, die von der REST-Schnittstelle beachtet werden müssen. In Festlegung Umsetzung geht es um festgelegte Technologien und automatische Code-Generierung. Im Anschluss wird in Querschnittliche Konzepte ein Überblick über den Umgang mit Ereignissen in der REST-API gegeben.

2. Grundlagen

REST steht für Representational State Transfer und ist ein Paradigma für die Softwarearchitektur von verteilten Systemen, insbesondere für Webservices (Wikipedia). Roy Fielding hat in seiner Dissertation die Grundlagen von REST-Services definiert. Diese lauten wie folgt:

Client-Server

REST-Services besitzen die Eigenschaften einer Client-Server-Architektur und folgen diesem Architekturstil.

Zustandslosigkeit

Ein REST-Service speichert zwischen zwei Anfragen keinerlei Zustandsinformationen. Jede Ressource muss alle Informationen enthalten, die für den Server bzw. Client notwendig sind, um sie verarbeiten zu können.

Caching

REST-Nachrichten sollen, entweder explizit oder implizit, angeben, ob sie sich für einen Cache eignen oder nicht.

Einheitliche Schnittstelle

REST-Schnittstellen besitzen vier Eigenschaften, um einheitlich beschaffen und leicht nutzbar zu sein.

Jede Ressource ist über einen URI eindeutig adressierbar. Damit geschieht der Zugriff für jeden Client eindeutig und für alle nachvollziehbar.

REST-Services können an ihre Clients unterschiedliche Repräsentationen der Ressourcen ausliefern, z. B. in verschiedenen Sprachen oder Formaten. Die Veränderung einer Ressource erfolgt nur über eine dieser Repräsentationen.

Jede Anfrage muss alle Informationen zu ihrer Verarbeitung mitliefern, also selbstbeschreibend sein. Beispiele dazu sind das explizite Kennzeichnen des übertragenen Formats oder die Verwendung von HTTP-Methoden.

Hypermedia as the Engine of Application State (HATEOAS): REST-Services dürfen bei ihren Clients nur minimale Kenntnisse der Schnittstelle voraussetzen. Insbesondere das Navigieren zwischen Ressourcen sollte über Links geschehen, die Teil vorausgehender Ressourcen sind. Die Umsetzung von HATEOAS erzeugt so eine Navigation zwischen Ressourcen ähnlich zur Navigation zwischen Webseiten über Hyperlinks.

Mehrschichtige Systeme

Für Aufrufer von REST-Services muss es transparent sein, welche Systeme zwischen dem Client und dem Server oder hinter der Schnittstelle liegen. Aufrufe ändern sich nicht durch Änderungen an der Infrastruktur (z. B. durch Maßnahmen zur Skalierung) oder durch technische Änderungen, welche nicht den REST-Service selbst betreffen.

Einen guten Überblick und Verweise auf detailliertere Beschreibungen bietet die Wikipedia-Seite zu REST. Für einen tiefen Einstieg in die Grundlagen bietet sich die Dissertation von Roy Fielding an.

Das Reifegradmodell von Leonard Richardson (Richardson Maturity Model, s. Abbildung 1) ordnet diese Eigenschaften vier Reifegraden zu.

richard maturity model.dn
Abbildung 1. Richardson Maturity Model

Die IsyFact zielt auf eine möglichst vollständige Umsetzung der Eigenschaften und peilt daher einen hohen Reifegrad für ihre REST-Services an.

Architekturregel

Ein auf der IsyFact basierendes IT-System muss seine REST-Services mindestens anhand Stufe zwei des Richardson Maturity Models umsetzen.

Konkret stellt Stufe 2 des Richardson Maturity Models folgende Anforderungen:

  • Ressourcen werden über einen eindeutigen URI angesprochen.

  • Ressourcen werden über HTTP-Methoden angesprochen und je nach HTTP-Methode abgefragt oder verändert.

  • Ressourcen melden ihren Status durch eine Abfrage oder Veränderung über HTTP-Statuscodes zurück.

Stufe 3 des Richardson Maturity Models stellt darüber hinaus weitere Anforderungen:

  • Ressourcen enthalten Verweise auf URIs, mit denen weitere Anfragen im Kontext der Ressource möglich sind.

Diese grundlegende Festlegung hat große Auswirkungen auf die einzelnen Aspekte der Ausgestaltung der REST-Webservices. Diese werden in den folgenden Kapiteln beschrieben und konkretisiert.

2.1. Ausnahmen (Erlaubte Abweichungen zum Reifegradmodell)

In Ausnahmefällen kann auf die Einhaltung der Vorgabe zum Reifegrad (mindestens Level 2) verzichtet werden.

Prozessorientierte Aufrufe - Geschäftsvorfälle

Ein Ausnahmefall stellt die Abbildung von prozessorientierten Aufrufen dar, die häufig durch die Migration von bestehenden prozessorientierten Schnittstellen (z.B. RPC, httpInvoker) entstanden sind. Die Abbildung auf eine ressourcenorientierte REST-Schnittstelle ist i.d.R. mit einer Neukonzeption der Anwendung unter einer ressourcenorientierten Denkweise verbunden. Der Aufwand für die Neukonzeption steht oft nicht im Verhältnis zum tatsächlichen (fachlichen) Nutzen. Aus diesem Grund dürfen prozessorientierte Aufrufe über einfache http-Aufrufe realisiert werden, die bis auf die Ressourcenorientierung und Verwendung der http-Verben, den übrigen Vorgaben des REST-Konzepts folgen. Diese http-Aufrufe entsprechen zwar nicht dem Reifegradmodell (Level 2), sind aber in diesem Ausnahmefall IsyFact-konform.

Wird über die Schnittstelle ein Geschäftsvorfall angestoßen, ist der Geschäftsvorfall in der URI zu adressieren:

Tabelle 1. Namenskonvention prozessorientierter URIs
Prozessorientierte URIs

Schema

/{geschaeftsvorfall}/{geschaeftsprozess}

Beispiele

/auskunft/personaliensuche

/auskunft/dokumentensuche

/auskunft/schluesselsuche

/meldung/erstmeldung

/meldung/aenderung

3. Eigenschaften von REST-Schnittstellen

3.1. Aufbau der Ressourcen-URIs

Jede Ressource eines REST-Services ist über einen URI eindeutig adressierbar. Dieser Abschnitt legt Konventionen für den Aufbau der URIs fest.

Dieses Konzept empfiehlt die Nutzung von HATEOAS.

Ressourcen repräsentieren Objekte und werden deshalb als Substantiv beschrieben. Die Bezeichnung muss möglichst einfach gewählt werden (z. B. der Name der Entität oder die Aufgabe der Ressource). Repräsentiert die Ressource eine Menge von Objekten, wird der Plural verwendet.

Der hierarchische Aufbau von Ressourcen ist explizit erwünscht:

Tabelle 2. Namenskonvention Ressourcen-URIs
Ressourcen-URIs

Schema

/{ressource}/{id}/{ressource}/{id}/{...}

Regeln

Die Bezeichnung der Ressource beinhaltet ausschließlich Kleinbuchstaben.

Zwischen Teilen zusammengesetzter Begriffe steht jeweils ein Bindestrich.

Beinhaltet die Bezeichnung Sonderzeichen, müssen sie gemäß üblicher Transkriptionsregeln ersetzt werden (z. B. "ae" statt "ä").

Beispiele

/kunden/12345/bestellungen/123/artikel

/nachrichten

Beispiele für eine Menge von Objekten.

Die Ressource liefert alle Objekte zurück.

/nachrichten/{id}

Beispiel für ein einzelnes Objekt. Die Ressource liefert ein eindeutig identifizierbares Objekt zurück.

/eingehende-nachrichten/

Beispiel für zusammengesetzte Begriffe.

/vertraege/

Beispiel für die Ersetzung von Sonderzeichen.

3.1.1. Beziehungen zwischen Ressourcen

Beziehungen zwischen Ressourcen können entweder über HATEOAS (vgl. Stufe 3 in Abbildung 1) oder über die URI selber abgebildet werden.

Tabelle 3. Namenskonvention Beziehungen zwischen Ressourcen
Beziehungen zwischen Ressourcen

Schema

/{ressourcen A}/{id von einer Ressource A}/{ressource(n) B}/{...}

Hinweis

Enthält eine Ressource wiederum mehrere Ressourcen, wird wieder der Plural verwendet.

Beispiele

/nachrichten/{id}/absender

adressiert den Absender einer bestimmten Nachricht.

/nachrichten/{id_n}/cc/{id_e}

adressiert den bestimmten CC-Empfänger (mit der Id id_e) einer bestimmten Nachricht (mit der Id id_n).

3.1.2. Adressierung von mehreren Ressourcen

Der Zugriff auf mehrere Ressourcen aus einer Menge erfolgt über eine kommaseparierte Liste von IDs.

Tabelle 4. Namenskonvention Adressierung von mehreren Ressourcen
Adressierung von mehreren Ressourcen

Schema

/{ressourcen}/{id1},{id2}

Beispiele

/nachrichten/{id1},{id2}

3.1.3. Query Parameter

Query Parameter werden ausschließlich für das Sortieren, Paginierung und Filtern von Ressourcen verwendet.

Beispiel Sortierung:

/nachrichten?sort=timestamp,ASC

Beispiel Filter:

/nachrichten?timestamp=2020-08-23&land=deutschland

Beispiel Paginierung:

/nachrichten?page=5&pageSize=15
Architekturregel

Es dürfen nur nicht datenschutzrelevante Informationen in Query Parametern verwendet werden, um das Loggen von datenschutzrelevanten Daten zu verhindern.

Für alle anderen Suchen sind POST-Requests zu verwenden, siehe Suche und Filterung mittels POST.

3.2. Verwendung von HTTP-Methoden

Die folgenden Kapitel beschreiben, welche HTTP-Methoden zu unterstützen sind und welche Funktion sie besitzen. Die übrigen HTTP-Methoden werden nicht verwendet.

GET wird verwendet, um eine Ressource zu lesen. Die Ressource wird dabei nicht verändert, wodurch diese Methode idempotent ist.

Idempotenz beschreibt die Möglichkeit, den gleichen HTTP-Request mehrfach an die Schnittstelle senden zu können, ohne dass ein anderes Ergebnis erzielt wird als bei einem einzelnen Request.

Listing 1. Beispiele für GET-Anfrage
GET /kunden/1234 HTTP/1.1
accept: text/html, application/json, application/xhtml+xml, application/xml;q=0.9, */*;
charset=utf-8
HOST: xx.yy.zz
ACCEPT-ENCODING: gzip, deflate, br
Listing 2. Beispiele für GET-Antwort
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
    "id":1234,
    "name":"Mustermensch",
    "adresse":"Musterstraße 1",
    "ort":"Musterstadt"
}

POST wird verwendet, um eine neue Subressource innerhalb einer Ressource anzulegen. Es ist beim Erstellen von Datensätzen in den meisten Fällen das Mittel der Wahl, da das Backend hierbei die ID vergibt. Dadurch, dass die Datensätze mit neuen IDs angelegt werden, wird bei jedem weiteren senden des Requests ein neuer Datensatz angelegt und die Methode ist nicht idempotent. Des Weiteren muss POST für komplexe Suchen und Suchen mit datenschutzrelevanten Informationen verwendet werden, siehe Suche und Filterung mittels POST.

Listing 3. Beispiele für POST-Anfrage
POST /kunden HTTP/1.1
HOST: xx.yy.zz
Content-Type: application/json; charset=utf-8
{
    "name":"Mustermensch",
    "adresse":"Musterstraße 1",
    "ort":"Musterstadt"
}
Listing 4. Beispiele für POST-Antwort
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
{
    "id":1234,
    "name":"Mustermensch",
    "adresse":"Musterstraße 1",
    "ort":"Musterstadt"
}
Architekturregel

POST wird auch für fachliche Operationen genutzt, die keiner der anderen HTTP-Methoden zugeordnet werden können (z. B. Verifikation eines Antrags).

PUT wird verwendet, um eine Ressource zu ändern oder zu erstellen. Beim Ändern wird die gesamte Ressource mitgesendet und nicht nur der zu ändernde Teil wie bei PATCH. Ist die Ressource mit der gesendeten ID noch nicht vorhanden, wird diese durch PUT erstellt. Dies ist aber zu vermeiden, da der Client keine IDs vergeben soll. PUT ist, da immer die gesamte Ressource geändert wird, bzw. die ID beim Erstellen schon vorgegeben ist, idempotent. Deshalb ist PUT PATCH beim Aktualisieren vorzuziehen. In Abhängigkeit davon, ob eine Ressource geändert oder neu erstellt wurde, wird der entsprechende Response Code (200 OK bzw. 201 Created) zurückgegeben.

Listing 5. Beispiele für PUT-Anfrage
PUT /kunden/1234 HTTP/1.1
HOST: xx.yy.zz
Content-Type: application/json; charset=utf-8
{
    "name":"Mustermensch",
    "adresse":"Musterstraße 1",
    "ort":"Beispielort"
}
Listing 6. Beispiele für PUT-Antwort
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
    "id":1234,
    "name":"Mustermensch",
    "adresse":"Musterstraße 1",
    "ort":"Beispielort"
}

PATCH wird verwendet, um eine Ressource mit einer bestimmten ID zu ändern. Hierbei werden nur die Felder gesendet, die geändert werden sollen und nicht die ganze Ressource. Das kann bei Ressourcen mit beispielsweise einem Auto-Increment Feld dazu führen, dass bei mehreren Patches unterschiedliche Ergebnisse erzielt werden. Deswegen kann Patch idempotent sein, muss aber nicht.

Listing 7. Beispiele für PATCH-Anfrage
PATCH /kunden/1234 HTTP/1.1
HOST: xx.yy.zz
Content-Type: application/json; charset=utf-8
{
    "adresse":"Neue Straße 2"
}
Listing 8. Beispiele für PATCH-Antwort
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
    "id":1234,
    "name":"Mustermensch",
    "adresse":"Neue Straße 2",
    "ort":"Beispielort"
}
Architekturregel

PATCH ist nur zu verwenden, wenn PUT aus triftigen Gründen nicht funktioniert.

DELETE wird verwendet zum Löschen einer Ressource. Da jede Ressource nur einmal gelöscht werden kann, ist diese Methode idempotent.

DELETE /kunden/1234

HEAD wird verwendet, um Meta-Informationen einer Ressource zu erhalten. Es gibt die gleichen Daten zurück wie GET nur ohne Response-Body und ist damit idempotent.

HEAD /kunden/1234

OPTIONS wird verwendet, um die von einer Ressource unterstützten Methoden anzuzeigen. Es ist idempotent, da nichts verändert wird.

OPTIONS /kunden
Eine Übersicht über die HTTP-Methoden und ihre Eigenschaften findet sich in Anhang: http-Methoden

3.2.1. Suche und Filterung mittels POST

Eine Filterung und Suche sollte im Regelfall über Query Parameter einer Anfrage mit dem HTTP-Methoden "GET" geschehen. Einige Abfragen sind jedoch zu komplex, um sie über HTTP GET abzubilden. Aspekte, die für eine Verwendung von POST statt GET sprechen sind folgende (ohne Anspruch auf Vollständigkeit):

  • Query Parameter sind zu lang und es besteht die Gefahr, dass so die gesamte URL zu lang ist und vom Provider nicht verarbeitet werden kann.

  • Query Parameter enthalten datenschutzrelevante Informationen, die aufgrund der Aufzeichnung von URLs in Logs o. ä. nicht in der URL übertragen werden dürfen.

  • Die Suche ist eine unscharfe Suche, bzw. sucht nicht nach Gleichheit, wie beispielsweise Timestamp größer als.

  • Die Abfrageparameter sind nicht über ein logisches UND verknüpft, sondern z. B. über ein logisches ODER.

In diesen Fällen muss die HTTP-Methode "POST" verwendet werden. Die Filter- und Suchkriterien werden in den Body der Anfrage aufgenommen.

3.3. Verwendung von HTTP-Statuscodes

Im Folgenden werden die HTTP-Statuscodes dargestellt, die zu verwenden sind.

HTTP-Statuscode Nachricht Erläuterung

200

OK

Wird als Ergebnis eines erfolgreichen HTTP-Requests zurückgeliefert.

201

Created

Wird als Ergebnis eines erfolgreichen HTTP-Requests zurückgeliefert, wenn eine neue Ressource angelegt wurde.

204

No Content

Wird als Ergebnis eines erfolgreichen HTTP-Requests zurückgeliefert, wenn der Request keinen Response-Body liefert.

304

Not Modified

Wird beim Caching eingesetzt und sagt dem Client, dass seine Ressource noch aktuell ist.

400

Bad Request

Der HTTP-Request enthält fehlerhafte Daten, z. B. XML nicht Schema-konform, es wurde ein Virus gefunden, die Anfrage enthält fachliche Fehler, etc.

401

Unauthorized

Es ist ein Fehler bei der Authentifizierung aufgetreten, z. B. falscher Benutzername/Passwort.

403

Forbidden

Der Benutzer hat nicht die erforderlichen Rechte.

404

Not found

Die angeforderte Ressource existiert nicht.

405

Method not allowed

Die aktuelle Operation ist auf der Ressource nicht erlaubt (beispielsweise PUT auf einer read-only Ressource).

406

Not Acceptable

Die Content Negotiation ist fehlgeschlagen.

409

Conflict

Die Ressource wurde zwischenzeitlich geändert.

500

Internal Server Error

Es ist ein interner Fehler bei der Bearbeitung der Anfrage aufgetreten.

Weitere Statuscodes sind in Abhängigkeit vom Fachverfahren möglich.

Für die Auswahl weiterer Statuscodes bietet die entsprechende Wikipedia-Seite zu HTTP-Statuscodes einen guten Startpunkt. Empfehlenswert ist außerdem diese gut strukturierte Übersicht von openapi-generator.tech, die Informationen von Wikipedia und des IETF übersichtlich aufbereitet.

3.4. Repräsentationen

REST-Services können verschiedene Repräsentationen derselben Ressource anbieten. Diese Repräsentationen sind entweder textbasiert oder binär.

Liegt eine Ressource textbasiert vor, müssen ihre Repräsentationen, z. B. durch ein Schema, validierbar sein. Ebenso muss jede Repräsentation den gleichen, fachlichen Inhalt umfassen.

Liegt eine Ressource binär vor, muss sie bei direkten Abfragen (über GET) binär ohne weitere Veränderungen (z. B. Einbettung in eine textbasierte Form oder zusätzliche Encodings) zurückgegeben werden. Nicht erlaubt ist eine Einbettung einer binären Ressource in eine textbasierte Repräsentation (z. B. ein BASE64-encodiertes Bild in einem dafür geschaffenen JSON- oder XML-Dokument). Ist eine binäre Ressource Teil einer textbasierten Ressource, so wird eine Einbettung ebenfalls nicht empfohlen. Stattdessen sollte der URI der binären Ressource in der textbasierten Ressource enthalten sein, um sie bei Bedarf direkt abzufragen.

Architekturregel

Alle REST-Services innerhalb einer Systemlandschaft nutzen eine einheitliche, textbasierte Repräsentationsform. Binäre Daten werden über direkte Anfragen binär ohne Transformation zurückgeliefert.

Diese Regel reduziert die Komplexität der internen Services und erhöht die Homogenität der Systemlandschaft. Ebenso fällt der Aufwand zur Pflege mehrerer, inhaltlich identischer Repräsentationsformen weg.

Die Kommunikation mit IT-Systemen außerhalb der Systemlandschaft sollte ebenfalls über eine festgelegte textbasierte Repräsentation geschehen. Hier sind jedoch gesetzliche Vorgaben, Rahmenbedingungen und die Anforderungen der externen Kommunikationspartner zu berücksichtigen.

Ein typisches Beispiel ist eine Systemlandschaft, die intern JSON als Repräsentation nutzt, nach außen hin aber XÖV-konforme Nachrichten schicken muss und daher teilweise XML als Repräsentation nutzt.

3.4.1. Content Negotiation

Liegen Ressourcen in mehreren Repräsentationen vor (z. B. als JSON- und XML-Dokumente oder Medien in Form von komprimierten Daten oder Rohdaten), geschieht die Auslieferung einer konkreten Repräsentation über HTTP Content Negotiation.

Weitere Details zu Content Negotiation bietet die entsprechende Spring Wikipedia-Seite.

REST-Services setzen in ihren Antworten einen fest definierten Inhaltstyp pro Repräsentation. Wenn für eine Repräsentation mehr als ein Inhaltstyp üblich ist, reagiert ein REST-Service auf eine Anfrage tolerant. D. h. er liefert auch dann die gewünschte Ziel-Repräsentation der angeforderten Ressource aus, falls in der Anfrage ein anderer MIME-Type mit dem gleichen inhaltlichen Ziel-Format angefordert wurde.

Anfragen geben ihre inhaltlichen Präferenzen im HTTP-Header Accept mit. Der REST-Service setzt den Inhaltstyp der Antworten im HTTP-Header Content-Type. Für die häufigsten Repräsentationen legt Anhang: Content types die erwarteten und zurückgegebenen Inhaltstypen fest.

3.5. Metadaten

Der Bereich Metadaten kann optional implementiert werden.

Zum Erhalten von Metadaten wird die HEAD-Methode verwendet. Die HEAD-Methode ist identisch zu GET, außer, dass sie keinen Response-Body zurückgibt.

3.6. Caching

Caching ist eine optionale Technik.

Wenn Caching eingesetzt werden soll, ist der Einsatz von ETags das Mittel der Wahl. ETags beschreiben die Version der Ressource, die angefragt wird. Ist der ETag im Header der Anfrage der gleiche, wie der ETag der gespeicherten Ressource, wird als Statuscode 304 zurückgegeben.

Zur Implementation wird in Spring der ShallowEtagHeaderFilter verwendet. Dieser reduziert allerdings nur die genutzte Netzwerk-Kommunikation, nicht aber die Rechenzeit im Server, da zum Vergleichen der ETags die Ressource geladen werden muss.

Weitere Caching-Methoden, die auch die Serverlast reduzieren, sind möglich.

Eine Übersicht, welche HTTP-Methoden Caching-Fähig sind, findet sich unter Anhang: http-Methoden

3.7. HATEOAS

Nach dem HATEOAS-Paradigma soll eine zurückgelieferte Ressourcen weiterführende URLs (vergleichbar mit HTML-Links) zu verwandten Ressourcen enthalten, anstelle die verwandten Ressourcen mit der angefragten Ressource zusammengeführt zu übermitteln.

Listing 9. Beispiel für eine HATEOAS-konforme Resource mit URIs zu weiterer Resource
{
    "kunde":"Alex Mustermensch",
    "kontostand":30,
    "vertrag":"https://base.url/kunde/1234/vertrag",
}

Folgende Vorteile ergeben sich daraus:

  • Der Client benötigt nur minimales Wissen über die Struktur der Services und der Datenstrukturen: Änderungen können somit leichter realisiert werden.

  • Der Server hat die Möglichkeit in Abhängigkeit vom Client Optionen auszublenden, indem z. B. einfach der Link nicht enthalten ist.

  • Die Bearbeitung der Anfrage auf der Serverseite ist kostengünstiger und performanter zu realisieren, da Daten nicht "zusammengesucht und -gesetzt" werden müssen.

  • Es muss in der Regel eine kleinere Nachricht übertragen werden.

Die letzten beiden Punkte führen direkt zu Fragestellungen des Schnittstellen- bzw. API-Designs:

Die letzten zwei Vorteile ergeben sich vor allem aus der Voraussetzung, dass der Client in der Regel die "zusätzlichen Daten" nicht benötigt, die nur über die URI als Referenz übermittelt werden. Werden diese zusätzlichen Daten nach jedem Aufruf der übergeordneten Ressource angefordert, werden zusätzliche technische Verarbeitungsschritte nötig (Client: Request für die neuen Daten aufbauen, versenden. Server: Anfrage & Berechtigung prüfen, Daten suchen, ggf. konvertieren, versenden,…).

Bei häufigen, parallelen Anfragen würde dieses Szenario dann einen hohen Overhead generieren. Hier ist im Einzelfall die Schnittstelle entsprechend dem gewünschten Trade-Off zu modellieren.

4. Festlegungen zur Umsetzung

4.1. Technologieauswahl

Die IsyFact legt folgende Technologien für die Umsetzung von REST-Schnittstellen (vgl. Tabelle 1) fest.

Tabelle 5. Frameworks für die Umsetzung
Framework Technologie-Stack Beschreibung

Spring Webflux

Java/Spring

Umsetzung von REST-Clients

(unter Nutzung von Apache HTTPComponents)

Spring MVC

Java/Spring

Umsetzung von REST-Services

(zur Nutzung mit Tomcat)

Angular Framework Modul: @angular/common/http

TypeScript/Angular

Umsetzung von REST-Clients

OpenAPI

technologie-unabhängig

Dokumentation der Service Schnittstelle und Erzeugung von Service und Client

4.2. Spring MVC im Vergleich zu Spring Webflux

Das Spring Framework stellt seit Version 5 Spring Webflux als reaktive Alternative zu Spring MVC bereit.

Mit Webflux ist es möglich, reaktive REST APIs zu implementieren. Beim reaktiven Programmierparadigma geht es um die Reaktion auf Veränderungen (Observer Pattern), im Fall von REST APIs um die Reaktion auf gesendete Daten. Das ist hilfreich, wenn man große Datenmengen (z. B. Mediendateien oder größere Ergebnismengen aus einer Datenbankabfrage) über das REST API übertragen möchte. Die Daten werden dann in mehreren Paketen gesendet, der Empfänger reagiert jeweils auf den Erhalt eines Pakets.

Der Hauptvorteil von Webflux liegt aber im geringeren Ressourcenverbrauch auf der Server Seite. Webflux basiert auf einem Event-Loop Mechanismus, während Spring MVC auf Thread Pools basiert. Der Performance Vorteil von Webflux macht sich insbesondere bei vielen gleichzeiten Service-Aufrufen und/oder der Übertragung größerer Datenmengen bei einem Service-Aufruf bemerkbar.

Trotzdem empfiehlt die IsyFact grundsätzlich, Spring MVC für die Umsetzung von REST-Services einzusetzen. Die Vorteile des reaktiven Programmierparadigmas erfüllen sich nur, wenn alle Teile der Umsetzung entsprechend gestaltet sind. Dies ist aktuell nicht der Fall.

Für REST-Clients wiederum empfiehlt die IsyFact die Verwendung des WebClient anstatt des RestTemplate, da letzteres nicht mehr aktiv weiterentwickelt wird.

Für die Anbindung von Angular-Clients enthält der Baustein Angular eine entsprechende Komponente.

4.3. Schnittstellendokumentation

Alle auf IsyFact basierten Anwendungen müssen ihre REST-Schnittstellen mit der OpenAPI 3.0 Spezifikation beschreiben. Sowohl YAML als auch JSON sind als Format der Schnittstellendokumentation zulässig. Für die Erstellung der Schnittstellendokumentation ist grundsätzlich kein besonderes Tool erforderlich, ein Texteditor ist ausreichend. Das OpenAPI Format wird jedoch von diversen Entwicklungsumgebungen (z. B. IntelliJ) unterstützt, was die Bearbeitung erleichtert.

4.3.1. Code-Generierung

Bei der Erstellung von REST-APIs gibt es grundsätzlich 2 Ansätze: Contract/API First oder Code First. Bei Contract/API First wird zunächst die Schnittstellenbeschreibung erstellt und daraus der Code (Server und Client) generiert. Bei Code First wird zuerst die API implementiert und mit Annotationen für die Schnittstellenbeschreibung versehen. Aus den Annotationen wird dann die Schnittstellenbeschreibung generiert.

Die IsyFact empfiehlt den Contract/API First Ansatz.

Für die Generierung des Codes von Server und Client sind die Standards von OpenApi 3.x zu beachten.

4.3.2. Bereitstellen von generierten Clients

Wie beschrieben können aus der OpenAPI Schnittstellenbeschreibung Services und Clients für diverse Plattformen generiert werden. Aus Convenience-Gründen wird empfohlen, dass Anbieter von Schnittstellen fertig generierte Clients für die gängigen Plattformen zur Verfügung stellen. Gängige Clients im Kontext der IsyFact sind Java (Spring) und Javascript (Angular).

4.4. Verwendung von Transportobjekten

Architekturregel

REST-Schnittstellen verwenden ausschließlich Transferobjekte (Data Transfer Objects, DTOs).

Die DTOs werden ebenfalls innerhalb der Schnittstellenbeschreibung schematisch beschrieben.

Der Code für die DTOs wird daraus generiert.

Innerhalb der Schnittstellenbeschreibung des Service werden die Schemata der DTOs üblicherweise als Referenz mittels #ref eingebunden. Die Referenz kann sich dabei auf eine Beschreibung innerhalb derselben Datei beziehen oder auch auf eine externe Datei. Die Datei kann dabei sowohl über einen Dateipfad als auch über eine URL adressiert werden.

Beispiel aus Swagger Petstore:

requestBody:
   content:
      application/json:
         schema:
            $ref: '#/components/schemas/Pet'

...

components:
   schemas:
      Pet:
         name:
            type: string
            example: doggie

5. Querschnittliche Konzepte

5.1. Validierung von Anfragen

Wie bereits gesagt muss die formale Validierung der Daten vor der inhaltlichen Validierung im Anwendungskern stattfinden. Genau genommen müssen die Daten formal korrekt sein, bevor sie überhaupt in irgendeiner Weise weiter verarbeitet werden können. Daher ist die formale Prüfung der erste Schritt in der Verarbeitung von Daten und komplett unabhängig von der weiteren Verarbeitung.

Eine geeignete Methode zur Prüfung der Daten auf formale Korrektheit ist die Validierung gegen das vorhandene Schema der Schnittstellenbeschreibung. Atlassian bietet den Swagger Request Validator als freie Software an (Apache 2.0 Lizenz). Damit lassen sich HTTP Requests und Responses gegen eine OpenAPI bzw. Swagger Spezifikation prüfen, OpenAPI v3 wird dabei bereits unterstützt. Der Swagger Request Validator hat keine weiteren Abhängigkeiten und kann somit unabhängig von HTTP APIs oder Frameworks verwendet werden.

5.2. Fehlerbehandlung

In diesem Abschnitt werden alle Aspekte zur Fehlerbehandlung in REST-Services beschrieben.

Bei der Fehlerbehandlung müssen zwei Varianten unterschieden werden: technische Fehler und fachliche Fehler.

Definition von technischen Exceptions: Service-Methoden deklarieren keine oder eine technische Exception. Die technische Exception muss für alle Service-Methoden einer Service-Schnittstelle gleich sein.

Definition von fachlichen Exceptions: Service-Methoden können beliebig viele fachliche Exceptions deklarieren. Diese können spezifisch für jede Service-Methode sein.

Übermittlung von Daten: Die Felder Ausnahme-ID, UUID und Fehlernachricht, wie in den Nutzungsvorgaben Fehlerbehandlung beschrieben, müssen stets übertragen werden. Weiterhin darf kein Stack-Trace übertragen werden.

Grundsätzlich sind die Vorgaben aus den Nutzungsvorgaben Fehlerbehandlung zu beachten. Im Anschluss folgen REST-Spezifische Regeln.

Technische Fehler werden in der HTTP-Response immer mit dem HTTP-Code 500 gesendet.

Fachlichen Fehlern muss ein entsprechend passender Fehlercode im Bereich 4xx zugeordnet werden.

Einheitliche Fehlermeldungen beschreibt den Inhalt einer Fehlermeldung.

5.2.1. Entkopplung von Fehlerbehandlung und Service-Logik

Die Fehlerbehandlung erfolgt nicht in der REST-Schnittstelle, sondern in einem separaten RestExceptionHandler, welcher die Exception Fassade für REST-Schnittstellen repräsentiert (vgl. Abbildung 2).

fehlerbehandlung.dn
Abbildung 2. Entkopplung Fehlerbehandlung und Service-Logik

Spring Webflux unterstützt ebenso wie Spring MVC die Annotation @RestControllerAdvice. Die Implementierung des RestExceptionsHandler kann daher über die Annotation einer Klasse mit @RestControllerAdvice erfolgen.

Details zur Implementierung können der offiziellen Spring Dokumentation entnommen werden.

5.2.2. Einheitliche Fehlermeldungen

Eine Fehlernachricht aus einer REST-Schnittstelle (egal, ob Client oder Server Error) muss mindestens folgende Informationen beinhalten.

Tabelle 6. Standardfelder für Fehlernachrichten
Feld Datentyp Beschreibung

timestamp

String

Exakter Zeitpunkt an dem der Fehler aufgetreten ist.

status

Integer

HTTP-Fehlercode

error

String

Kurzbeschreibung des Fehlers, z. B. Entity not found.

message

String

Detailbeschreibung des Fehlers. Bei Client Errors sollte beschrieben werden wie der Client den Fehler beheben kann. Es sollen keine technischen/internen Details (z. B. Stacktrace) in der Beschreibung enthalten sein. Die message setzt sich, wie in den Nutzungsvorgaben Fehlerbehandlung beschrieben, folgendermaßen zusammen:

#AusnahmeId Fehlertext #UUID

path

String

Pfad der Ressource, die in der Anfrage adressiert wurde.

Fehler werden in einem Array zurückgegeben um auch das Senden mehrerer Fehler, beispielsweise bei der Validierung von Eingaben, zu ermöglichen.

[
    {
     "timestamp": "2020-08-23T14:53:33.452+02:00",
     "status": 404,
     "error": "Not Found",
     "message": "#0001 Die Nachricht mit der ID=1 konnte nicht gefunden werden. #4321",
     "path": "/ressource/1234"
    }
]

Sollten bei der Verarbeitung mehrere Fehler auftreten, sind alle Fehler zurückzugeben.

Exceptions werden im RestExceptionHandler auf den jeweiligen HTTP-Statuscode und die entsprechende Nachricht gemapped. Welche Informationen enthalten sein dürfen wird im Nutzungsvorgaben Fehlerbehandlung genauer beschrieben.

5.3. Sicherheit

Die Authentifizierung wird nach OAuth2 sichergestellt. Hierfür muss der Client mit jeder Nachricht einen Bearer Token mitsenden. Dieser wird im Request-Header mit dem Tag "Authorization" versehen. Im Sicherheitsbaustein wird das Token an den SecurityContextHolder weitergegeben. Dort werden die Daten ausgelesen und der Token wird auf Echtheit überprüft. Bei einer reinen Weitergabe greift die Bearer Propagation.

Nähere Informationen zu OAuth2 gibt es auf der OAuth Homepage.

Informationen zur Bearer Propagation gibt es in der offiziellen Spring Dokumentation.

Um eine Klasse oder einzelne Methoden zu sichern, wird empfohlen, die @Secured-Annotation von Spring Security in der Service-Schicht zu verwenden. Die Verwendung auf einzelnen Webcontroller Klassen/Methoden wird nicht empfohlen. Für Webcontroller ohne Service Schicht kann Request Level Autorisierung genutzt werden.

Generell ist das Konzept der Sicherheitsarchitektur zu beachten.

5.4. Logging

Eingehende und ausgehende Nachrichten sind an der Systemgrenze zu loggen. Für die Erstellung der Log-Einträge ist der Baustein Isy-Logging zu verwenden. Abweichungen zum Konzept von Isy-Logging sind im Folgenden aufgeführt.

5.4.1. Server / Service-Provider

Der Baustein bietet mit @Systemgrenze eine Annotation, die einen Interceptor aufruft, der automatisch Aufrufe aller Methoden in der annotierten Klasse (z. B. Controller) fachlich loggt. (vgl. Nutzungsvorgaben Logging Kapitel 4.2.2.1) Für ein technisches Logging soll zusätzlich ein weiterer Interceptor verwendet werden.

Dieser Interceptor soll folgende Nachrichten am Controller loggen:

  • eingehende Anfragen (eingehende Nachrichten),

  • ausgehende Antworten (ausgehende Nachrichten).

5.4.2. Client / Service-Consumer

In der Klasse, welche die Aufrufe an den Service-Provider stellt, sollen

  • ausgehende Anfragen (ausgehende Nachrichten),

  • eingehende Antworten (eingehende Nachrichten),

geloggt werden.

Wie auch serverseitig, sollen diese Log-Nachrichten möglichst automatisiert via AOP erstellt werden.

5.4.3. Inhalt des Log-Eintrags für eine Nachricht

Um die Nachrichten im Zuge der Log-Auswertung zu filtern, sind nachrichtenspezifische Ereignisschlüssel zu verwenden.

Zusätzlich zu den Anforderungen an die Inhalte einer Log-Nachricht Konzept Logging Kapitel 4.1.1), sollen folgende Informationen als Marker in der Log-Nachricht enthalten sein:

Tabelle 7. Standard für zu erfassende Logging-Informationen
Die zu protokollierende Information ist abhängig, ob es ein Request oder Response ist

Ein-/Ausgehende Nachricht

immer protokollieren

Request-URL (Address + URI)

immer protokollieren

Query String

immer protokollieren

HTTP-Methode

immer protokollieren

Protocol

immer protokollieren

Connection- z. B. keep-alive, Transfer-Encoding

immer protokollieren

Encoding

immer protokollieren

Accept

immer protokollieren

Content-Type

immer protokollieren

Content-Length

immer protokollieren

Aufgerufene Methode der Klasse

immer protokollieren (REST-Controller-Methode)

Zeitpunkt

bei ausgehend: Sende-Zeitpunkt
bei eingehend: Empfangszeitpunkt-Zeitpunkt

Dauer der Verarbeitung

bei eingehend: Dauer vom Empfang des Requests bis zum Versand der Antwort
bei ausgehend: Dauer vom Versand des Requests bis zum Empfang der Antwort

Aufgerufenes Nachbarsystem

bei ausgehendenden Requests, sonst leer

Konfigurationsparameter

Diese Konfigurationsparameter aus Kapitel 4.2.2.1 Nutzungsvorgaben Logging sollen für den neuen Interceptor ebenfalls existieren:

  • loggeDauer

  • loggeDaten

  • loggeDatenBeiException

  • loggeMaximaleParameterGroesse

Nachrichten Logging

Zum Test und Debuggen einer Anwendung soll es möglich sein, dass komplette Nachrichten in ihrem Rohformat inkl. dem kompletten Header geloggt werden. Das Aktivieren dieses Loggings ist über einen Konfigurationsparameter steuerbar.

5.5. Überwachung

Die IsyFact stellt zur Überwachung von Anwendungen den Baustein Überwachung bereit.

Die Grundlagen der Überwachung von IsyFact basierten Anwendungen werden im Konzept Überwachung beschrieben.

Detailliertere Informationen zur Konfiguration und zur Verwendung des Bausteins Überwachung findet man in den Nutzungsvorgaben Überwachung. Dieses Dokument beinhaltet auch das Kapitel "Informationen von Services". Dieses Kapitel beinhaltet eine Liste der zu überwachenden bzw. anzubietenden Informationen für die Services von Anwendungen. Die dort aufgelisteten Informationen müssen für jeden Service einzeln angeboten werden. Die dort beschriebenen Informationen zur Überwachung gelten in gleichem Maße auch für REST Services.

5.6. Versionierung

Versionierung kann auf verschiedene Weisen stattfinden.

Prinzipiell gilt es inkompatible API-Änderungen in einer REST-Schnittstelle zu vermeiden. Falls irgendwann eine inkompatible Änderung notwendig sein sollte, ist eine Versionierung in der URL zu verwenden.

Die Version steht immer vor dem Pfad der Ressource und beinhaltet ausschließlich die Major Version.

https://service.de/api/v1/messages/{id}
https://service.de/api/v2/messages/{id}

Anhang A: Content Types

Die folgende Tabelle enthält alle Inhaltstypen, die vom Baustein REST der IsyFact vorgesehen und unterstützt sind.

Tabelle 8. Zuordnung von Repräsentationen zu Content-Type- und Accept-Headern
Repräsentation Content-Type Accept

Textbasierte Repräsentationen

JSON (ohne HATEOAS)

application/json

application/json

JSON (mit HATEOAS)

application/hal+json

application/hal+json
application/json

XML

application/xml

application/xml
text/xml

Binäre Repräsentationen

PDF

application/pdf

application/pdf

JPG

image/jpg

image/jpg

Anhang B: HTTP-Methoden

Tabelle 9. Übersicht HTTP-Methoden und ihre Eigenschaften

Methode

Idempotent

Cacheable

GET

Ja

Ja

POST

Nein, kann und sollte aber Idempotent umgesetzt werden

Möglich

PUT

Ja

Nein

PATCH

Nein, kann und sollte aber Idempotent umgesetzt werden

Nein

DELETE

Ja

Nein

HEAD

Ja

Ja

OPTIONS

Ja

Nein