In der letzten Ausgabe haben wir verschiedene API-Architekturstile untersucht, von denen jeder seine einzigartigen Stärken hat. Trotz der vielen Optionen bleibt REST die beliebteste. Seine Popularität bedeutet jedoch nicht, dass es einfach ist. REST definiert lediglich Ressourcen und die Verwendung von HTTP-Methoden. Um die Kunst der Erstellung von REST-APIs zu beherrschen, müssen wir bestimmte Richtlinien befolgen, um sicherzustellen, dass wir effiziente, benutzerfreundliche APIs entwerfen.
In dieser Ausgabe behandeln wir die feineren Details des REST-API-Designs. Das beinhaltet:
-
API-Probleme aufspüren. Wir lernen, die verräterischen Anzeichen ineffizienter APIs zu erkennen. Die „schlechten Gerüche“ weisen auf die Notwendigkeit einer Neugestaltung oder Verbesserung hin.
-
API-Reife verstehen. Wir beschäftigen uns mit dem Richardson Maturity Model (RMM), einem Modell, das uns hilft, gutes von schlechtem API-Design zu unterscheiden, indem es beurteilt, wie gut eine API mit dem REST-Framework übereinstimmt.
-
Zurück zum Wesentlichen. Um sicherzustellen, dass wir alle auf dem gleichen Stand sind, betrachten wir noch einmal die Kernkomponenten von REST-APIs – HTTP-Verben und Statuscodes.
Um diese Konzepte zum Leben zu erwecken, beginnen wir mit dem Entwurf einer Anmelde- und Anmeldekomponente als erstes der drei praktischen Beispiele in dieser Ausgabe. In unserer nächsten Ausgabe werden wir weitermachen und untersuchen, wie man eine Warenkorb-API erstellt, und uns mit der Neugestaltung der Stripe-Zahlungs-API befassen.
Wie können wir erkennen, wenn eine API ihr Potenzial nicht ausschöpft und ein Redesign benötigt? Ähnlich wie Code können APIs einen deutlichen „Geruch“ abgeben, wenn sie nicht die erwartete Leistung erbringen. Es ist von entscheidender Bedeutung, diese Warnsignale zu erkennen. Schauen wir uns einige konkrete Szenarien an, die darauf hindeuten, dass eine API möglicherweise nicht den Anforderungen entspricht.
Angenommen, wir haben die API-Dokumentation gründlich studiert, haben aber immer noch Schwierigkeiten, die Nuancen ihrer Funktionalität zu verstehen. Dieser ständige Klärungsbedarf seitens der API-Besitzer ist ein klarer Hinweis darauf, dass eine API benutzerfreundlicher sein könnte.
Betrachten Sie ein weiteres Beispiel, bei dem API-Parameter und -Ergebnisse vage definiert sind. Dieser Mangel an Klarheit kann zu Verwirrung und möglichen Fehlern führen. Es verlangsamt die Entwicklung und behindert eine effektive Zusammenarbeit zwischen Teams.
Denken Sie auch an eine Situation, in der unsere Front-End- und Back-End-Teams intensiv zusammenarbeiten müssen, nur um das API-Verhalten zu testen und zu validieren. Dieser hohe Koordinationsaufwand lässt auf eine API schließen, die nicht so intuitiv oder gut dokumentiert ist, wie sie sein sollte.
Als API-Besitzer bemerken wir möglicherweise unterschiedliche Warnsignale. Wenn wir uns ständig mit Anfragen zur API-Nutzung konfrontiert sehen, ist es, als ob wir einen Teilzeit-Kundendiensthut aufsetzen würden. Dieses Szenario weist auf eine API hin, die von einer detaillierteren Dokumentation und möglicherweise einem intuitiveren Design profitieren könnte.
Ein weiteres verräterisches Zeichen kann ein Zustrom von Anfragen nach kleineren Verbesserungen sein. Wenn wir das Gefühl haben, dass wir unsere APIs ständig optimieren und anpassen, könnte das darauf hindeuten, dass sie nicht so robust oder flexibel sind, wie sie sein müssten.
Diese Beispiele aus der Praxis verdeutlichen die Art von Herausforderungen, die darauf hindeuten, dass unsere APIs eine Feinabstimmung gebrauchen könnten.
Das Richardson-Reifegradmodell (RMM) [3], eingeführt von Leonard Richardson im Jahr 2008, dient als wertvolles Werkzeug, um das Konzept der API-Reife besser zu verstehen und wie gut eine API den REST-Konzepten entspricht. Dieses Modell hilft uns, die Stärken und Schwächen unseres API-Designs zu identifizieren.
Das RMM definiert vier Ebenen, um zu beurteilen, wie genau eine API dem REST-Framework entspricht. Die Hauptfaktoren, die über die Reife eines Dienstes entscheiden, sind sein URI, HTTP-Methoden und HATEOAS (Hypermedia als Engine des Anwendungsstatus).
Auf Ebene 0 verwenden APIs einen einzelnen URI und eine einzige HTTP-Methode, typischerweise POST. Dieser Ansatz nutzt nicht die wahren Fähigkeiten des HTTP-Protokolls und es fehlt eine einheitliche Möglichkeit zur Interaktion mit Systemressourcen. Martin Fowler nannte dieses Level aufgrund seines vereinfachten RPC-ähnlichen Systems bekanntermaßen „The Swamp of POX (Plain Old XML)“.
Level 1 führt in das Konzept der Ressourcen ein, einen Eckpfeiler des RESTful-Designs. Jede Ressource wird durch einen URI eindeutig identifiziert, wodurch die Verwaltung und Interaktion mit verschiedenen Elementen eines Systems einfacher wird. Allerdings wird immer noch nur eine HTTP-Methode, POST, verwendet, wodurch das volle Potenzial von REST eingeschränkt wird.
Level 2 stellt einen Fortschritt im RESTful-Design dar. Die Dienste auf dieser Ebene verwenden nicht nur eindeutige URIs für Ressourcen, sondern nutzen auch verschiedene HTTP-Methoden (wie GET, POST, PUT, DELETE), die Vorgängen auf diesen Ressourcen entsprechen. Dieser Ansatz macht unsere APIs intuitiver und passt sie besser an die Prinzipien des Webs an. Dieser Reifegrad ist der beliebteste.
Level 3 bringt das Konzept von HATEOAS (Hypermedia as the Engine of Application State) ein. HATEOAS macht unsere APIs selbstbeschreibend und verbessert so ihre Benutzerfreundlichkeit und Auffindbarkeit. Wenn ein Client mit einer Ressource interagiert, stellt die API nicht nur Informationen über die Ressource selbst bereit, sondern auch über verwandte Ressourcen und mögliche Aktionen, die alle durch Hypermedia-Links dargestellt werden.
Wenn wir im obigen Beispiel eine Abfrage für das Konto 12345 anfordern, erhalten wir nicht nur den Kontostand (100 US-Dollar), sondern die Antwort leitet uns auch durch die nächsten Schritte und deren Ausführung über URIs. Beispielsweise könnten wir mehr Geld auf das Konto 12345 einzahlen, indem wir zu /account/12345/deposit navigieren.
Das RMM bietet ein effektives Framework, das uns hilft, RESTful-Prinzipien besser zu verstehen und in unserem API-Design zu implementieren. Bei der Verbesserung unserer APIs ist es wichtig zu bedenken, dass Level 2 eine Voraussetzung für REST ist [3].
Beim Entwerfen eines Systems stoßen wir häufig auf zwei Schichten: die Webdienstschicht, die für die Bearbeitung von Webanfragen verantwortlich ist, und die Dienstschicht, in der die eigentliche Arbeit stattfindet, wie z. B. die Interaktion mit Datenbanken, die Bearbeitung von Nachrichtenwarteschlangen und die Kommunikation mit anderen Diensten .
Auf der Webdienstebene nutzen wir HTTP-Verben. Diese Verben definieren die Operationen, die wir für verschiedene Ressourcen ausführen können. Auf der Serviceebene verwenden wir CRUD-Operationen (Erstellen, Lesen, Aktualisieren, Löschen), um diese Operationen zu definieren.
Das folgende Diagramm listet die gebräuchlichen HTTP-Verben und ihre Zuordnungen zu Dienstmethoden auf. Es ist wichtig zu verstehen, dass es nicht immer eine direkte Eins-zu-eins-Zuordnung zwischen HTTP-Verben und Dienstmethoden gibt. Beispielsweise kann das GET-Verb verwendet werden, um eine einzelne Ressource oder eine ganze Liste von Ressourcen abzurufen. Ebenso können sowohl PUT- als auch PATCH-Verben zum Ändern einer Ressource verwendet werden. Das PATCH-Verb wird jedoch speziell verwendet, wenn wir teilweise Änderungen an einer Ressource vornehmen möchten.
Während die fünf HTTP-Methoden die meisten unserer Anforderungen erfüllen sollten, kann es Szenarios geben, in denen wir benutzerdefinierte Vorgänge definieren müssen. Wir haben zwei Möglichkeiten, dies zu erreichen:
-
Ordnen Sie benutzerdefinierte Vorgänge Standard-HTTP-Verben zu. Zum Beispiel könnten wir a abbilden suchen Operation zum GET-Verb. Dies könnte jedoch zu Verwirrung führen, da das Verb möglicherweise nicht vollständig mit dem Zweck der Operation übereinstimmt. Zur Klärung dieser Zuordnungen ist eine umfassende API-Dokumentation von entscheidender Bedeutung.
-
Definieren Sie eine benutzerdefinierte HTTP-Methode. In einigen Fällen benötigen wir möglicherweise einen bestimmten Vorgang, der nicht durch Standard-HTTP-Verben abgedeckt wird. Beispielsweise kann es bei Online-Spielen erforderlich sein, ein Spiel zurückzusetzen. Zu diesem Zweck können wir eine benutzerdefinierte Methode wie RESET definieren.

REST basiert auf dem HTTP-Protokoll. Daher sollten unsere APIs HTTP-Statuscodes verwenden, um ein konsistentes und vorhersehbares Verhalten sicherzustellen. Das folgende Diagramm zeigt einige gängige HTTP-Statuscodes.

Allerdings sind HTTP-Statuscodes nicht erschöpfend für alle möglichen Ergebnisse eines API-Aufrufs. Wir sollten auch unsere eigenen internen Statuscodes oder Fehlercodes einrichten, um bestimmte servicebezogene Probleme zu kommunizieren. Eine effiziente Möglichkeit zur Verwaltung dieser Codes besteht darin, eine gemeinsame Bibliothek für alle Codes im System zu verwalten. Es ermöglicht eine einfache Registrierung und gemeinsame Nutzung über verschiedene Code-Repositorys hinweg.
Nehmen wir beispielsweise an, dass der Benutzerdienst Nachrichtencodes im Bereich von 50000 bis 59999 verwendet, während der Warenkorbdienst Codes von 60000 bis 69999 verwendet. Wir können diese Nachrichtendetails in eine HTTP-Antwort einschließen und sie an die Clients zurücksenden. Diese Vorgehensweise kommt dem Kundenservice enorm zugute, da er Probleme anhand des zurückgegebenen Codes leicht identifizieren kann.
Eine goldene Regel, die wir immer befolgen sollten, besteht darin, für jede Antwort einen Code zurückzugeben, auch wenn es sich um eine Zeitüberschreitung handelt. Die Einhaltung dieser Regel gewährleistet ein vorhersehbares und konsistentes Verhalten. Dies ist entscheidend für die Aufrechterhaltung einer qualitativ hochwertigen Dienstleistung.