Herausforderungen bei Implementierung und Einsatz von Web-APIs

Web-APIs einsetzen: Eigentlich ganz einfach, oder?
Keine Kommentare

Web-APIs erfreuen sich großer Beliebtheit, bieten sie doch eine scheinbar perfekte Lösung zur Systemintegration. Jedoch gibt es beim Einsatz auch einige Fallstricke, die unbedingt bedacht werden sollten.

Web-APIs führen zu einer recht engen Kopplung, weshalb unter anderem eine Strategie für den Umgang mit vorübergehend nicht verfügbaren Kommunikationspartnern benötigt wird. Weitere Aspekte sind die Unzuverlässigkeit des Netzwerks oder auch die Tatsache, dass SSL in zahlreichen Produktivumgebungen keine ausreichende Sicherheit bietet. Dieser Artikel diskutiert die wichtigsten Herausforderungen beim Einsatz von Web-APIs und zeigt Wege auf, wie sie gemeistert werden können.

Web-APIs einsetzen: Eigentlich ganz einfach, oder?

Web-APIs sind aus der Softwareentwicklung derzeit nicht wegzudenken. Zahlreiche Unternehmen bieten über öffentlich zugängliche, webbasierte APIs den Zugriff auf Daten und Dienste an, und Behörden stellen zunehmend große Informationsmengen zur Verfügung. Aber auch zur Systemintegration innerhalb eines Unternehmens haben sich Web-APIs inzwischen – neben alternativen Lösungsansätzen – ihren festen Platz gesichert. Und schließlich haben Web-APIs auch durch den Trend zu Microservices-Architekturen einen weiteren Schub erfahren. Hier gilt es, die Kommunikation zwischen den einzelnen Microservices zu implementieren. Dabei kommen sehr verbreitetet Web-APIs zum Einsatz.

Dabei ist der Begriff Web-API an sich gar nicht so leicht zu greifen. Wie so oft gibt es durchaus unterschiedliche Definitionen und Ansichten, was genau ein Web-API ist, wodurch es sich auszeichnet oder welche Regeln es einhalten sollte. Diese Diskussion soll hier jedoch nicht geführt werden. Im Kontext des Artikels soll der Begriff Web-API eher allgemein definiert sein: als eine Schnittstelle auf Basis von Webtechnologien, insbesondere HTTP.

Solche Schnittstellen lassen sich mit aktuellen Frameworks glücklicherweise recht schnell implementieren. Ganz gleich, ob man Spring oder Java EE einsetzt oder vielleicht eines der vielen anderen verfügbaren Frameworks. In aller Regel können Entwickler schon nach recht kurzer Zeit einen ersten Prototyp des neuen Web-API auf einem Testserver bereitstellen. Diese Mächtigkeit heutiger Frameworks ist nicht hoch genug zu bewerten. Im Vergleich zu früheren Zeiten müssen Entwickler nun deutlich weniger Code schreiben, um zum gleichen Ergebnis zu kommen. Dies ermöglicht nicht nur ein agileres Vorgehen, sondern bedeutet auch, dass entsprechend mehr Zeit bleibt, um sich auf die Fachlichkeit zu konzentrieren.

Es besteht jedoch gleichzeitig die Gefahr, dass die Entwicklung der Schnittstelle dadurch unterschätzt wird. Denn es scheint auf den ersten Blick eine leichte Aufgabe zu sein, ein Web-API zu erstellen. Doch dies ist ein Trugschluss! Je länger ein Projekt andauert, je mehr Anforderungen hinzukommen, je besser die Anwendung getestet und je länger sie betrieben wird, desto mehr zeigt sich, dass viele Aspekte bedacht werden müssen, und dass dabei so einige Fallen lauern. Denn auch wenn Frameworks vieles erleichtern und verbergen, mindestens eine Komplexität bleibt auf jeden Fall erhalten: die Vielzahl an Herausforderungen, die verteilte Systeme mit sich bringen. Hierzu zählen unter anderem Kopplung, Fehlerbehandlung, Zuverlässigkeit, Wiederholbarkeit und Sicherheit. Leider haben es Entwickler oft schwer, insbesondere gegenüber dem Management zu begründen, weshalb die Implementierung eines guten Web-API daher doch viel mehr Aufwand erfordert, als man nach der raschen Bereitstellung einer ersten funktionsfähigen Version annehmen könnte.

Vorsicht bei enger Kopplung

Zunächst ist festzustellen, dass eine auf HTTP basierende Kommunikation zu einer recht engen Kopplung zwischen den Kommunikationspartnern führt. Diese resultiert aus der direkten Punkt-zu-Punkt-Verbindung zwischen Clients und dem Anwendungsendpunkt, der das Web-API bereitstellt. So ist zwingend erforderlich, dass beide Kommunikationspartner gleichzeitig gestartet und verfügbar sind. Sie kommunizieren synchron und müssen sich gegenseitig kennen. Vor diesem Hintergrund ist die häufig geäußerte Einschätzung, REST-Schnittstellen führten zu einer losen Kopplung, zumindest kritisch zu hinterfragen. Denn Kopplung kann in verschiedenen Varianten auftreten. Und während sich für Web-APIs in manchen Kriterien sicherlich eine eher lose Kopplung feststellen lässt, sind zumindest die vorgenannten Charakteristiken eindeutige Anzeichen für eine enge Kopplung.

Es ist wichtig festzuhalten, dass eine enge Kopplung zwischen Kommunikationspartnern nicht automatisch etwas Schlechtes darstellt. Im Gegenteil, enge Kopplung hat durchaus auch positive Aspekte. Allerdings bringt sie eben auch verschiedene Nachteile mit sich, die bei der Entwicklung von Web-APIs bedacht werden sollten. Beispielsweise ist eine Strategie notwendig für solche Fälle, in denen der Kommunikationspartner vorübergehend nicht verfügbar ist oder wenn asynchrone Aufgaben mittels synchroner Kommunikation angestoßen und verfolgt werden sollen.

Eine besonders problematische Situation kann entstehen, wenn eine größere Anzahl von Systemen eng miteinander gekoppelt werden, wenn also beispielsweise alle diese Systeme über Punkt-zu-Punkt-Verbindungen miteinander kommunizieren. In einem Systemverbund resultieren Requests an ein System oftmals in Folge-Requests an weitere Systeme und diese möglicherweise wiederum in weitere Requests innerhalb des Verbunds. Mit der Zeit entsteht so ein kaum noch zu überblickender und beherrschender Abhängigkeitsgraph. Gerade bei enger Kopplung können sich lokale Probleme eines Systems dann recht schnell auf zahlreiche andere Systeme auswirken. Es entsteht ein Lawineneffekt, der Systemverbund wird zu einem fragilen Gebilde. Eine solche Entwicklung geschieht meist schleichend und über einen längeren Zeitraum. Es ist daher wichtig, sich dieser Problematik bewusst zu sein, um von Beginn an Gegenmaßnahmen ergreifen zu können. Beispielsweise könnten zumindest punktuell alternative Kommunikationslösungen wie etwa Messaging eingesetzt werden, die in einer loseren Kopplung resultieren.

Resilienz

Entwickelt man eine Anwendung, die über das Netzwerk kommuniziert, dann ist es wichtig, sich immer wieder vor Augen zu führen, welche zahlreichen Herausforderungen verteilte Systeme mit sich bringen. Für jede einzelne dieser Herausforderungen ist eine Strategie zu entwickeln. All die schönen Bibliotheken und Frameworks, die Entwicklern heute zur Verfügung stehen, befreien zwar oberflächlich von der Last, sich mit den technischen Details der Netzwerkkommunikation auseinanderzusetzen: Ein Methodenaufruf genügt, und schon wird ein Request über HTTP versandt. Doch Fehler, Ausfälle und Verbindungsschwierigkeiten sind keine Ausnahme, sondern die Regel! Und mit aktuellen Trends wie Cloud oder Microservices werden die Architekturen sogar noch komplexer. Die Vielfalt der potenziellen Fehlerursachen ist beachtlich. Mal ist das Netzwerk bloß langsam, ein anderes Mal kommt eine Verbindung überhaupt nicht zustande. Systeme sind überlastet oder fallen aus. Zertifikate oder Lizenzen sind versehentlich abgelaufen. Mobile Clients verlieren urplötzlich ihre Verbindung. Diese Liste ließe sich beliebig fortsetzen. Daher müssen auch API-Clients widerstandsfähig sein, um unerwartete Probleme überstehen und behandeln zu können. Aufseiten des API-Betreibers sollte sichergestellt sein, dass lokale Fehler in einem Teilbereich des API sich nicht ausbreiten und keine negative Auswirkung auf das Gesamtsystem haben. In jedem Fall aber sollte der Resilienz der einzelnen Systeme hohe Beachtung zukommen.

Fehlerfälle und Wiederholbarkeit

Die meisten Web-APIs ermöglichen nicht nur den lesenden Zugriff auf bestehende Daten, sondern auch deren Modifizierung sowie das Anlegen neuer Daten oder Ressourcen. Oftmals erlauben es Web-APIs darüber hinaus, länger laufende Prozesse entweder direkt anzustoßen oder deren spätere asynchrone Ausführung zu beauftragen. In solchen Fällen muss man sich mit der Frage beschäftigen, was geschehen soll, wenn eine solche Anfrage einmal scheitert. Was passiert also beispielsweise, wenn ein Client auf einen HTTP Request zur Erstellung einer neuen Ressource oder auf einen Request zur Ausführung eines fachlichen Prozesses keine Antwort erhält? Während lesende Requests frei von Seiteneffekten sein sollten und im Fehlerfall daher einfach wiederholt werden können, ist dies bei modifizierenden Requests nicht so einfach möglich. Denn aus Sicht des Clients lässt sich nicht zweifelsfrei feststellen, ob der initiale Request beim API angekommen ist und verarbeitet wurde, ob also lediglich die Antwort verloren ging, oder ob der Request schon gar nicht beim API ankam und somit auch noch keinerlei Aktivitäten erfolgt sind. Ein einfaches Wiederholen des Requests kann dann zu unerwünschten Nebeneffekten führen, wie beispielsweise dem Erzeugen von Duplikaten durch mehrmaliges Anlegen der gleichen Daten oder der mehrmaligen Ausführung fachlicher Prozesse. Ein möglicher Lösungsansatz könnte sein, jedem Request eine eindeutige ID hinzuzufügen. Diese ID müsste dann vom Web-API protokolliert und gespeichert werden, sodass duplizierte Requests erkannt werden können und sichergestellt ist, dass der gleiche Request nicht mehrmals ausgeführt wird.

Ist die Erkennung von Duplikaten gelöst, schließt sich die nächste, nicht minder wichtige Frage an. Sie beschäftigt sich mit der Wiederholbarkeit fehlgeschlagener Requests. Resultiert ein modifizierender Request in einem Fehler, der auch erfolgreich an den Client übermittelt wurde, kann dieser Fehler entweder grundsätzlicher Natur sein oder nur vorübergehend auftreten. Während grundsätzliche Fehler, etwa unsinnige oder unvollständige Requests, auch im Wiederholungsfall abgelehnt würden, lohnt bei vorübergehenden Fehlerursachen (z. B. bei technischen Problemen) gegebenenfalls ein erneuter Versuch des identischen Requests. Daher sollte die Erkennung von Duplikaten aufseiten des Empfängers aber so gestaltet sein, dass duplizierte Requests immer dann zugelassen werden, wenn frühere Versuche des gleichen Requests fehlgeschlagen sind. Weiterhin sollte sichergestellt sein, dass im Fehlerfall sämtliche möglicherweise bereits erfolgten Teilschritte der Request-Verarbeitung zurückgenommen werden, damit ein wiederholter Empfang des gleichen Requests nach einem Fehlerfall wieder vom gleichen Ausgangszustand ausgehen kann. Diese komplette Rücknahme sämtlicher Teilschritte ist insbesondere in stark verteilten Architekturen, wie etwa bei Microservices, oftmals nicht trivial, da in der Regel keine Transaktion existiert, die alle Aktivitäten umschließt und die einfach zurückgerollt werden könnte. Denn werden einzelne Teilschritte von anderen Services erbracht, die wiederum selbst mittels eines HTTP Requests beauftragt werden, dann ist der Versand dieses Requests an keine Transaktion gebunden.

Schließlich ist eine Strategie festzulegen, wie fehlgeschlagene Requests zu wiederholen sind. In welchen Fehlerfällen sollen Wiederholungen überhaupt durchgeführt werden und in welchen nicht? Bei fehlgeschlagener Authentifizierung können Wiederholungen beispielsweise sogar zusätzliche Probleme schaffen, etwa dadurch, dass ein Account aufgrund mehrmaliger gescheiterter Authentifizierungsversuche gesperrt wird. Wie häufig sollten Wiederholungen maximal erfolgen oder über welchen Zeitraum, bevor der Versand eines Requests für endgültig gescheitert erklärt wird? Und wie lange sollte man nach einem gescheiterten Request abwarten, bevor man es ein weiteres Mal versucht? Ein System, das vorübergehend überlastet ist, könnte etwa durch zu häufige Wiederholungen endgültig in die Knie gezwungen werden. All diese Fragen sind nicht pauschal zu beantworten, sondern stark vom konkreten Anwendungsfall und seinem Umfeld abhängig. In jedem Fall sollten die entsprechenden Einstellungen aber nicht fest codiert, sondern konfigurierbar sein. Bei der Umsetzung solcher Strategien helfen gegebenenfalls bewährte Patterns wie Circuit Breaker oder Bulkhead [1] und frei verfügbare Bibliotheken, wie etwa das häufig angeführte Hystrix aus dem Softwarestack von Netflix.

Sicherheit nach innen und nach außen

Ein weiterer wichtiger Aspekt von Web-APIs ist das Thema Sicherheit. Wiederum sind hier nicht nur solche Schnittstellen zu betrachten, die der Anbindung externer Systeme dienen. Auch die Kommunikation zwischen Systemen innerhalb eines Unternehmens oder die Kommunikation von Microservices untereinander sollte tunlichst abgesichert werden.

Wer über die Sicherheit HTTP-basierter Kommunikation nachdenkt, landet unweigerlich bei SSL/TLS. SSL sollte aufgrund bekannt gewordener Schwachstellen heute nicht mehr eingesetzt werden. Stattdessen kommt in der Regel TLS zum Einsatz. Dennoch ist SSL der weiter verbreitete Begriff, und meist ist TLS gemeint, wenn von SSL gesprochen wird. HTTPS (Hypertext Transfer Protocol Secure) bezeichnet die Verwendung von HTTP über SSL oder TLS.

Die Absicherung der Kommunikation umfasst unterschiedliche Aspekte. Als wichtigste Eigenschaft wird meist die Vertraulichkeit der Verbindung angesehen, die durch Verschlüsselung erreicht wird. HTTPS bietet hier auf den ersten Blick eine verhältnismäßig leicht umzusetzende Lösung an, da die Verbindung zwischen Client und Endpunkt mittels SSL verschlüsselt wird. Betrachtet man jedoch gängige IT-Architekturen für produktive Systeme, sieht die Lage plötzlich etwas anders aus. Denn tatsächlich kommunizieren Clientsysteme in aller Regel nicht direkt mit dem Endpunkt eines Web-API. Stattdessen sind meist Reverse Proxies zwischengeschaltet. Diese können allerlei Aufgaben übernehmen, wie etwa Sicherheitsfunktionen, Adressumsetzung, Caching oder Load Balancing zwischen mehreren Instanzen der Anwendung. Beim Einsatz von Reverse Proxies kommt es nun darauf an, wie sie konfiguriert sind und betrieben werden. Eine verbreitete Variante ist jedoch, dass die Kommunikationsverbindung mit dem Client vom Reverse Proxy terminiert wird (SSL Termination). Die verschlüsselte Verbindung endet also an diesem Knoten. Der Reverse Proxy leitet die Anfrage dann mittels einer neuen separaten Verbindung zum Anwendungsserver weiter. Doch selbst wenn auch diese zweite Verbindung zwischen Reverse Proxy und Anwendungsserver wieder verschlüsselt erfolgt, so sind die vom Client gesendeten Daten doch mindestens auf dem Reverse Proxy im Klartext vorhanden. Dies stellt einen lohnenswerten Angriffspunkt dar. Selbstverständlich kommt es stark auf die Art der Anwendung und die Natur der versendeten Daten an, ob dies ein kritisches Sicherheitsproblem darstellt oder eher ein zu vernachlässigendes. Tatsache ist jedoch, dass ein solches Szenario in einigen Branchen und Anwendungsfällen keine ausreichende Sicherheit bietet. Hier gilt es also abzuwägen und gegebenenfalls über Alternativen nachzudenken.

Welche Alternativen gibt es? Zum einen erlauben es viele Reverse Proxies, einen sogenannten SSL-Pass-through-Modus zu konfigurieren. In diesem Modus wird die Verbindung mit dem Client nicht terminiert und eingehende Requests folglich auch nicht entschlüsselt. Stattdessen leitet der Reverse Proxy die TCP-Pakete – basierend auf seiner Konfiguration – einfach an andere Server weiter. Während dieser Betriebsmodus also die Sicherheit erhöht, geht dabei andererseits die Möglichkeit verloren, die Inhalte eingehender Requests vom Reverse Proxy inspizieren zu lassen, und darauf aufbauend, die erwähnten inhaltsbasierten Funktionen auszuführen.

Eine andere Alternative besteht darin, nicht die Verbindung zwischen Client und Endpunkt zu verschlüsseln, sondern die Daten selbst. Erfolgt die Kommunikation beispielsweise im JSON-Format, müssten Clients in diesem Fall ein zu versendendes JSON-Konstrukt vor dem Versand verschlüsseln und die empfangende Anwendung diese Daten entsprechend wieder entschlüsseln. Dies sorgt für Zusatzaufwände während der Entwicklung, löst aber dafür das Problem möglicher Angriffe auf dem Transportweg. Gegebenenfalls könnte man derart verschlüsselte Daten sogar über eine ungesicherte Verbindung, also ohne den Einsatz von SSL, versenden.

Neben der Vertraulichkeit der Kommunikation ist auch die Authentifizierung ein wichtiger Sicherheitsaspekt. Darunter versteht man, dass die Identität des Kommunikationspartners überprüfbar ist. Was den Server betrifft, so sind Sicherheitszertifikate ein verbreitetes und sinnvolles Mittel. Zur Identifizierung des Clients sind dagegen mehrere Varianten denkbar. Häufig werden Anmeldedaten als Bestandteil von Requests verschickt. Diese Variante ist wieder leicht umzusetzen und daher weit verbreitet. Jedoch hält sie nicht jedem Securityaudit stand. Denn bei diesem Ansatz wird ein Geheimnis, nämlich typischerweise das Passwort des Clients, über das Netzwerk verschickt. Selbst wenn dieser Versand über einen verschlüsselten Transportkanal erfolgt, gibt es Branchen und Anwendungen, in denen diese Art der Authentifizierung als nicht ausreichend sicher angesehen wird. Denn es existieren diverse Angriffsmöglichkeiten, wie Session Hijacking oder Replay-Attacken. Eine mögliche Alternative stellen Tokens dar, bei denen Anmeldedaten und gegebenenfalls weitere charakteristische Merkmale zu versendender Requests in codierter Form eingebettet werden. Eine gängige Variante sind etwa JSON Web Tokens.

Eine andere Möglichkeit besteht darin, Clientzertifikate einzusetzen. Doch auch diese haben ihre eigenen Herausforderungen. So ist die Frage zu klären, wie Clientzertifikate verteilt werden, insbesondere falls einzelne (oder sogar alle) Clients nicht im Vorhinein bekannt sind. Darüber hinaus wird eine Strategie benötigt, um abgelaufene Clientzertifikate zu ersetzen; und zwar idealerweise schon vor Ende ihres Gültigkeitszeitraums und nicht erst dann, wenn die Kommunikation nicht mehr funktioniert. Eine mögliche Lösung könnte sein, dass Clients die verbleibende Gültigkeitsdauer ihrer Zertifikate selbständig in regelmäßigen Abständen prüfen und bei Unterschreitung einer gewissen Gültigkeitsdauer eine Mitteilung an einen geeigneten Empfänger senden.

Viele Unternehmen setzen auch auf API-Management-Lösungen, die für so typische Aufgaben wie Authentifizierung in der Regel einen oder mehrere vorgefertigte Ansätze anbieten. Als API-Provider muss man so lediglich noch einige Konfigurationseinstellungen vornehmen, die eigentliche Implementierung des Authentifizierungsmechanismus wird jedoch vom Anbieter der API-Managementlösungen erbracht.

Kommunikationsmuster ändern, Last senken

Ein ganz anderer Aspekt, der ebenfalls bedacht werden sollte, ist die zu erwartende Kommunikation zwischen Clients und Web-API, die daraus resultierende Last auf Netzwerk und Anwendungsserver und wie sich diese gegebenenfalls reduzieren lässt, um die Gesamtperformanz zu verbessern. Nicht zuletzt lassen sich durch eine reduzierte Last meist die Betriebskosten senken. Und oftmals profitieren vor allem mobile Clients von einer effizienteren Kommunikation, da sie über schwankende Verbindungsqualität und möglicherweise auch über ein begrenztes Datenvolumen verfügen.

Wichtigster Ansatzpunkt ist hierbei der Abruf von Daten und Informationen. So sollten Clients in ihren Anfragen möglichst feingranular angeben können, an welcher Teilmenge oder welchem Detailgrad der Gesamtdaten sie tatsächlich interessiert sind. Dies kann etwa mittels einer (optionalen) Kombination mehrerer Filterkriterien geschehen. Gegebenenfalls kann sogar eine Abfragesprache zum Einsatz kommen, die es Clients erlaubt, selbst komplexe Filter für ihre Anfragen zu definieren. Zwar könnten Clients die empfangenen Daten auch selbst filtern oder reduzieren, effizienter ist es jedoch meist, die Filterung dort vorzunehmen, wo die Daten gespeichert sind, und nur die wirklich benötigten Daten an den Client zu schicken.

Ebenso lohnt ein Blick auf Polling-Szenarien, bei denen Clients in regelmäßigen Abständen den aktuellen Stand der Daten abfragen. Beispiele hierfür sind etwa sich regelmäßig verändernde Werte wie Finanzdaten, Zwischenergebnisse von Sportereignissen, die Auslastung technischer Systeme oder der Status lang laufender Prozesse, die durch einen früheren Request angestoßen wurden (Bestellungen, komplexe Berechnungen etc.) Je nachdem, wie viele Clients gleichzeitig ausgeführt werden und in welchen Abständen sie pollen, kann durch solche Requests erhebliche Last entstehen.

Ein erster Schritt zur Reduzierung dieser Last ermöglicht die Nutzung von Caching-Mechanismen wie HTTP ETag. Dabei sendet der Anwendungsserver in der Antwort zur ersten Anfrage eines Clients neben den eigentlichen Daten ein zusätzliches Headerfeld, das einen für den aktuellen Datenstand spezifischen ETag-Wert enthält. Bei einer erneuten Anfrage der gleichen Daten sendet der Client diesen ETag-Wert an den Server zurück. Nur wenn sich in der Zwischenzeit die Daten, und somit auch deren ETag-Wert, geändert haben, sendet der Server erneut den aktuellsten Stand der Daten an den Client. Haben sich die Daten jedoch nicht verändert, so antwortet er mit einer leeren Antwort und dem HTTP-Status 304 (not modified).

Mithilfe solcher Mechanismen kann zumindest verhindert werden, dass unveränderte Daten immer wieder an den gleichen Client gesendet werden. Die Datenmenge wird also reduziert, die (potenziell große) Anzahl von Requests bleibt dagegen unverändert. Doch auch diese binden wertvolle Ressourcen, um verarbeitet und beantwortet zu werden. Um hier eine deutliche Verbesserung zu erzielen, könnte beispielsweise das Kommunikationsmuster derart geändert werden, dass Clients nur einmalig (oder zumindest deutlich seltener) Daten anfordern und anschließend vom Server immer dann informiert werden, wenn Datenänderungen oder bestimmte Ereignisse vorliegen. Mit einer solchen Strategie kommt deutlich weniger Kommunikation zustande, nämlich im Wesentlichen immer nur dann, wenn es tatsächlich etwas mitzuteilen gibt.

Kostenlos: Das iJS React Cheat Sheet

Sie wollen mit React durchstarten?
Unser Cheatsheet zeigt Ihnen alle wichtigen Snippets auf einen Blick.
Jetzt kostenlos herunterladen!

Download for free

 

API Summit 2018

From Bad to Good – OpenID Connect/OAuth

mit Daniel Wagner (VERBUND) und Anton Kalcik (business.software.engineering)

Für webbasierte Clients oder mobile Anwendungen bieten sich zur Umsetzung beispielsweise Long Polling, Server-Sent Events oder ein Protokollupgrade auf WebSocket an. Zwar vertreten manche Entwickler die Ansicht, man könne beim Einsatz solcher Kommunikationsansätze eigentlich nicht mehr von einem reinen Web-API sprechen. Wie eingangs erwähnt, soll der Begriff im Kontext dieses Artikels jedoch weiter gefasst sein.

Im Fall einer Server-zu-Server-Kommunikation bieten sich alternativ WebHooks an. Dabei handelt es sich um ein nicht standardisiertes Verfahren, das jedoch unter anderem von einigen populären Diensten wie GitHub oder Slack angeboten wird. Hierbei stellt die anfragende Anwendung einen URL bereit, der die datenliefernde Anwendung kontaktieren kann, wenn Datenänderungen oder Ereignisse vorliegen. Das bedeutet, der Kommunikationskanal zur Übermittlung geänderter Daten wird in diesem Fall vom Web-API initiiert.

Wartbarkeit und Versionierung früh einplanen

Ein weiterer wichtiger Aspekt bei der Entwicklung von Web-APIs betrifft die Frage, wie sichergestellt wird, dass die Schnittstelle im Fall neuer Anforderungen möglichst einfach weiterentwickelt werden kann. Im Grunde stellen sich hier die gleichen grundsätzlichen Herausforderungen wie bei allen Arten von Schnittstellen auch: Sobald eine Schnittstelle veröffentlicht und von Clients verwendet wird, kann sie nicht mehr so leicht geändert werden. Denn zumindest solche Änderungen, die nicht rückwärtskompatibel sind, erfordern dann gleichzeitig eine Änderung oder den Austausch der Clients. Dies lässt sich noch einigermaßen bewerkstelligen, solange das API und seine Clients vom gleichen Team oder wenigstens im gleichen Unternehmen entwickelt und betrieben werden. Stehen die Clients jedoch nicht unter eigener Kontrolle, sondern werden beispielsweise von Geschäftspartnern entwickelt, bedeuten nicht kompatible Änderungen des API in aller Regel recht hohen Aufwand für alle Beteiligten. Denn es müssen gleichzeitige Deployments unterschiedlicher Systeme koordiniert werden, und das über Unternehmensgrenzen hinweg. Praktisch unmöglich wird dieses Unterfangen sogar, wenn nicht einmal sämtliche Clients (bzw. ihre Entwickler) bekannt sind. Dies kann geschehen, wenn ein Web-API keinen geschlossenen Benutzerkreis hat, sondern der Öffentlichkeit zur Verfügung gestellt wurde.

Ein verbreiteter Lösungsansatz besteht in der Einführung einer Versionierungsstrategie für die Schnittstelle beziehungsweise das Web-API. Neue Versionen werden eindeutig gekennzeichnet, angekündigt und dokumentiert. Clients müssen in ihren Anfragen anzeigen, welche Version des Web-API sie ansprechen möchten. Es gibt unterschiedliche Ansätze, versionierte APIs in der Praxis umzusetzen. Eine Möglichkeit besteht darin, unterschiedliche Versionen des API jeweils auf einem separaten Endpunkt oder einem separaten URL bereitzustellen. Dies bedeutet allerdings einen entsprechenden Mehraufwand im Betrieb und wirft die Frage auf, wie viele ältere Versionen man eigentlich anbieten möchte und über welchen Zeitraum. Darüber hinaus widerspricht diese Strategie auch dem REST-Prinzip, dass jede Ressource einen eindeutigen URL haben sollte. Sofern man also das Ziel verfolgt, ein RESTful Web-API zu implementieren, scheidet dieser Ansatz ohnehin aus. Alternativ dazu kann man mehrere Versionen auch auf dem gleichen Endpunkt und unter dem gleichen URL betreiben. In diesem Fall könnten Clients beispielsweise durch einen speziellen Header oder einen Parameter in ihren Requests anzeigen, welche Version des API sie ansprechen möchten. Nach Empfang eines Requests müsste das Web-API dann entsprechend verzweigen. REST unterstützt dieses Vorgehen mittels Content Negotation.

Eine alternative Strategie besteht darin, gar keine Versionierungsstrategie einzuführen. So gibt es zahlreiche Stimmen, die darin weniger eine Lösung als vielmehr die Erschaffung neuer Probleme erkennen. Anstatt also größere Anstrengungen in das Thema Versionierung zu investieren, könnte man stattdessen überlegen, wie ein Web-API zu entwerfen wäre, damit notwendige Änderungen möglichst rückwärtskompatibel sind. Erweiterungen eines existierenden API, also zusätzliche Daten und Operationen, sind in aller Regel rückwärtskompatibel umsetzbar. Gegebenenfalls fehlende Daten müssten lediglich durch sinnvolle Standardwerte ersetzt werden. Probleme schaffen dagegen in der Regel nur Breaking Changes, also vor allem Änderungen an existierenden Operationen und Daten, zum Beispiel Umbenennungen, Entfernungen oder abweichende Funktionalität. Die Kunst besteht darin, das Web-API von Beginn an so zu entwerfen, dass es leicht erweiterbar ist und neue Anforderungen möglichst keine Änderungen an bestehender Funktionalität erfordern. Doch ganz gleich, ob die Wartbarkeit der Schnittstelle nun mit oder ohne Versionierungsstrategie erreicht werden soll – in jedem Fall ist es wichtig, sich über diese Fragestellung Gedanken zu machen, bevor das Web-API veröffentlicht wird. Denn nachträglich lassen sich weder ein Versionierungsschema leicht einführen, noch ein API so umgestalten, dass es leichter erweiterbar ist. Beides sollte von Beginn an gegeben sein.

Auch bei der Entwicklung von Clients kann man sich mit Blick auf künftige API-Änderungen das Leben vom ersten Tag an entweder leichter oder eben potenziell schwerer machen. Beliebt bei Entwicklern sind etwa Codegeneratoren, die aus API-Spezifikationen (zum Beispiel im Swagger-Format) im Handumdrehen Codefragmente für den Einsatz in API-Clients erstellen. Ebenfalls beliebt ist der Einsatz von Data-Binding-Frameworks, die den Inhalt empfangener XML- oder JSON-Nachrichten zur Laufzeit automatisiert in fachliche Objekte umwandeln. Beides hat seine Daseinsberichtigung und spart zweifellos eine Menge Zeit bei der Entwicklung – jedenfalls initial. Jedoch zeigt die Erfahrung, dass sowohl Codegenerierung als auch Data-Binding-Frameworks häufig zu Problemen führen, wenn der API-Betreiber auch nur kleinste Änderungen vornimmt. Ein Paradebeispiel hierfür sind Felder, für die die API-Spezifikation eine definierte Menge gültiger Werte beschreibt. Die vorgenannten Werkzeuge setzen solche Felder oftmals als Enumeration um. Fügt der API-Betreiber zu einem späteren Zeitpunkt einen zusätzlichen gültigen Wert für das Feld hinzu, scheitert die automatische Umwandlung in die Enumeration zur Laufzeit, da die generierte Enumeration den neuen Wert ja nicht kennt und enthält. Sie muss daher ebenfalls aktualisiert werden. Und dies gilt auch dann, wenn der betreffende Client den neuen Wert tatsächlich gar nicht benötigt. Entwickler sollten daher zumindest erwägen, etwas mehr Aufwand in die Implementierung von API-Clients zu stecken, das ein oder andere doch lieber von Hand zu programmieren und sich insbesondere das Tolerant-Reader-Patterns zu Herzen nehmen.

Benutzbarkeit und Dokumentation

Schließlich sollte ausreichend Zeit eingeplant und verwendet werden, um sicherzustellen, dass das eigene Web-API leicht benutzbar ist. Es sollte einfach verständlich sein, also sinnvolle Namen und Bezeichnungen verwenden, und nach Möglichkeit ganz natürlich zur Nutzung unterschiedlicher Operationen in einer sinnvollen Reihenfolge hinführen. Eine aktuelle, ausreichend detaillierte und leicht zugängliche Dokumentation sollte ebenfalls selbstverständlich sein. Die Praxis zeigt, dass Entwicklerteams, die ein Web-API bereitgestellt haben, oft zahlreiche Supportanfragen von Entwicklern der Clientsysteme zu bewältigen haben. Je besser die vorstehenden Aufgaben erfüllt sind, je leichter das Web-API also verständlich und zu benutzen ist, desto weniger solcher Anfragen sind zu erwarten. Eine gute Benutzbarkeit und Dokumentation liegen daher vor allem auch im Interesse des Web-API-Betreibers.

Fazit

Web-APIs eigenen sich hervorragend als Mittel zur Umsetzung von Integrationslösungen, sowohl für die Integration unterschiedlicher Systeme als auch für die Kommunikation innerhalb eines Systems, etwa im Falle einer Microservices-Architektur. Zu den Vorteilen des Ansatzes zählen der Einsatz bewährter Technologien wie HTTP und SSL, die schnelle Umsetzbarkeit erster Prototypen, vergleichsweise leichtes Debugging und die breite Verfügbarkeit von Erfahrungen, Leitfäden und Tools. Während die Erstellung eines Web-API mit aktuellen Frameworks recht schnell von der Hand geht, wird jedoch leicht übersehen, dass die Entwicklung eines robusten, sicheren und wartbaren Web-API das Entwicklerteam durchaus vor einige Herausforderungen stellt. Gleiches gilt für die API-Clients. Wichtige Fragen sind zu klären, weitreichende Entscheidungen zu treffen. Diese betreffen die resultierende enge Kopplung der Systeme, Fehlerbehandlung und Wiederholbarkeit von Requests, das Sicherheitskonzept, die Begrenzung der Last oder auch die Frage, wie ein Web-API wartbar bleibt, sodass eine Weiterentwicklung nicht notwendigerweise zum Austausch aller existierenden Clients führen muss. Die Erstellung von Web-APIs birgt also durchaus einige Fallstricke, zu deren Umschiffung dieser Artikel hoffentlich ein wenig beitragen konnte.

 

Literaturverzeichnis

  • [1] Bulkhead: Nygard, Michael T.: „Release It!. Design and Deploy Production-Ready Software“, O’Reilly

Entwickler Magazin

Entwickler Magazin abonnierenDieser Artikel ist im Entwickler Magazin erschienen.

Natürlich können Sie das Entwickler Magazin über den entwickler.kiosk auch digital im Browser oder auf Ihren Android- und iOS-Devices lesen. In unserem Shop ist das Entwickler Magazin ferner im Abonnement oder als Einzelheft erhältlich.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu:
X
- Gib Deinen Standort ein -
- or -