Vorgaben und Konventionen für REST-Services
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:
Ressourcen-URIs | |
---|---|
Schema |
|
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 |
Beispiele für eine Menge von Objekten. Die Ressource liefert alle Objekte zurück.
Beispiel für ein einzelnes Objekt. Die Ressource liefert ein eindeutig identifizierbares Objekt zurück.
Beispiel für zusammengesetzte Begriffe.
Beispiel für die Ersetzung von Sonderzeichen. |
1.1. Beziehungen zwischen Ressourcen
Beziehungen zwischen Ressourcen können entweder über HATEOAS (vgl. Stufe 3 im Richardson Maturity Model) oder über die URI selber abgebildet werden.
Beziehungen zwischen Ressourcen | |
---|---|
Schema |
|
Hinweis |
Enthält eine Ressource wiederum mehrere Ressourcen, wird wieder der Plural verwendet. |
Beispiele |
adressiert den Absender einer bestimmten Nachricht.
adressiert den bestimmten CC-Empfänger (mit der Id |
1.2. Adressierung von mehreren Ressourcen
Der Zugriff auf mehrere Ressourcen aus einer Menge erfolgt über eine kommaseparierte Liste von IDs.
Adressierung von mehreren Ressourcen | |
---|---|
Schema |
|
Beispiele |
|
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
Für alle anderen Suchen sind POST-Requests zu verwenden, siehe Suche und Filterung mittels POST.
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.
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
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.
POST /kunden HTTP/1.1
HOST: xx.yy.zz
Content-Type: application/json; charset=utf-8
{
"name":"Mustermensch",
"adresse":"Musterstraße 1",
"ort":"Musterstadt"
}
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
{
"id":1234,
"name":"Mustermensch",
"adresse":"Musterstraße 1",
"ort":"Musterstadt"
}
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.
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"
}
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.
PATCH /kunden/1234 HTTP/1.1
HOST: xx.yy.zz
Content-Type: application/json; charset=utf-8
{
"adresse":"Neue Straße 2"
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"id":1234,
"name":"Mustermensch",
"adresse":"Neue Straße 2",
"ort":"Beispielort"
}
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
Die folgende Tabelle zeigt eine Übersicht über 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 |
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. Verwendung von HTTP-Statuscodes
Statuscodes bieten eine effektive Möglichkeit zur gezielten Steuerung des Client-Verhaltens. Die konkrete Nutzung der beschriebenen und weiterer Statuscodes sind in Abhängigkeit von den Anforderungen anwendungsspezifisch festzulegen.
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. |
202 |
Accepted |
Die Anfrage wurde empfangen und verstanden, aber wird noch asynchron bearbeitet. Die Anfrage ist zum Zeitpunkt der Antwort möglicherweise noch nicht abgeschlossen, aber der Server hat sie zur weiteren Bearbeitung angenommen. |
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. |
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. |
Ziele der Nutzung von Statuscodes in Verbindung mit einem Response-Mechanismus sind eine effiziente Gestaltung von API-Interaktionen, das Sicherstellen einer reibungslosen Nutzererfahrung und das Vermeiden von unnötiger Netzwerklast.
Durch den gezielten Einsatz passender Statuscodes in Verbindung mit Response-Headern
können Clients beispielsweise erkennen, ob ein erneuter Request sinnvoll ist, sie warten sollten oder ein Fehler in der Anfrage vorliegt.
Damit Clients automatisiert und sinnvoll auf API-Antworten reagieren können, muss die Implementierung der Statuscodes dem tatsächlichen Verarbeitungszustand entsprechen.
Response-Mechanismus | Sinnvolle Statuscodes | Anmerkung |
---|---|---|
|
|
Einzusetzen, wenn eine spätere Wiederholung der Anfrage sinnvoll ist. |
Status-URLs |
|
Zur initialen Bereitstellung von Informationen, wo der Status einer Anfrage bei asynchroner Verarbeitung abgefragt werden kann. |
Detaillierte Fehlermeldungen |
|
Ermöglicht es dem Client, Fehlerursachen gezielt zu erkennen und zu beheben. |
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.
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. |
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 Dokumentation. |
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
.
Die folgende Tabelle enthält alle Inhaltstypen, die von der IsyFact vorgesehen und unterstützt sind.
Repräsentation | Content-Type | Accept |
---|---|---|
Textbasierte Repräsentationen |
||
JSON (ohne HATEOAS) |
|
|
JSON (mit HATEOAS) |
|
|
XML |
|
|
Binäre Repräsentationen |
||
|
|
|
JPG |
|
|
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.
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: Übersicht über HTTP-Methoden und ihre Eigenschaften. |
7. HATEOAS
Nach dem HATEOAS-Paradigma soll eine zurückgelieferte Ressource weiterführende URLs (vergleichbar mit HTML-Links) zu verwandten Ressourcen enthalten, anstelle die verwandten Ressourcen mit der angefragten Ressource zusammengeführt zu übermitteln.
{
"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.