Umsetzung von REST-Services
1. Technologieauswahl
Die IsyFact legt folgende Frameworks für die Umsetzung von REST-Services fest.
| 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) |
Spring RestClient |
Java/Spring |
Umsetzung von REST-Clients |
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 |
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 empfiehlt die IsyFact die Verwendung des synchronen RestClient, um die Kompatibilität zu bestehenden Bausteinen der IsyFact aufrecht zu halten.
Bei der Verwendung des asynchronen WebClient sind die Einschränkungen reaktiver Verarbeitung mit IsyFact-Bausteinen zu beachten.
Für die Anbindung von Angular-Clients enthält der Baustein Angular eine entsprechende Komponente.
3. Umsetzung von REST-Clients
Die IsyFact empfiehlt für REST-Clients den RestClient aus dem Spring Framework ab Spring Framework 6.1 (Spring Boot 3.2).
| Client | Beschreibung | Einordnung durch die IsyFact |
|---|---|---|
|
blockierend, fluide API |
empfohlen ab Spring Framework 6.1 |
|
blockierend, Template-Method-API |
nicht empfohlen; seit Spring Framework 7.0 als |
|
nicht-blockierend, reaktiv, fluide API |
nicht empfohlen; bei durchgängig reaktiver Fachlogik nur in Ausnahmefällen einsetzbar, sofern die Einschränkungen im Fachverfahren abgefangen und in der Architekturentscheidung dokumentiert sind. |
Der RestClient ist der Nachfolger des RestTemplate, welcher nicht mehr aktiv weiter entwickelt wird, und ersetzt diesen weitgehend.
Beide bauen auf denselben Abstraktionen des Spring Framework auf:
-
ClientHttpRequestFactory/SimpleClientHttpRequestFactoryals Anbindung an die HTTP-Client-Bibliothek -
HttpMessageConverterfür Serialisierung und Deserialisierung -
ClientHttpRequestInterceptorfür die Querschnittserweiterung von Anfragen -
UriBuilderFactoryfür die URI-Erzeugung -
RestResponseErrorHandlerfür die Fehlerbehandlung
Bestehende Erweiterungen - insbesondere die in den Bausteinen isy-logging, isy-security und isy-task genutzten Interceptor- und Fehlerbehandlungsmechanismen - können daher nach Umstellung auf den RestClient übernommen werden.
Der RestClient folgt dem imperativen Programmiermodell der IsyFact: Die Anfrage wird synchron auf dem aufrufenden Thread ausgeführt und beendet.
Die für den WebClient beschriebenen Einschränkungen durch Thread-Wechsel treten also nicht auf.
|
Der Listing 1. Code-Beispiel RestClient
|
Seit Spring Framework 7.0 ist RestTemplate als deprecated markiert und wird aus künftigen Versionen entfernt.
Zu einer Migration gemäß der Anleitung Migrating to RestClient wird daher geraten.
Die Spring-Dokumentation empfiehlt bei der Migration eine zweistufige Vorgehensweise.
-
Das komponentenweise Ersetzen von
RestTemplate- durchRestClient-Instanzen:RestClient restClient = RestClient.create(restTemplate); -
Replikation der
RestTemplate-Instanziierung unter Verwendung von äquivalenten Methoden, die derRestClient.Builderbereitstellt (siehe Code-Beispiel RestClient).
|
Den Listing 2. WebClient blockierend
|
4. Einschränkungen reaktiver Verarbeitung mit IsyFact-Bausteinen
Die folgenden Einschränkungen gelten für die reaktive Verarbeitung mit Project Reactor, insbesondere beim Einsatz des WebClient.
Bei Nutzung des RestClient treten sie nicht auf.
Der WebClient basiert auf Project Reactor und verarbeitet HTTP-Anfragen in einem reaktiven, nicht-blockierenden Ausführungsmodell.
Innerhalb einer reaktiven Pipeline kann Project Reactor zwischen Threads wechseln:
Ein Request wird auf einem Thread begonnen und nach einer asynchronen Operation auf einem anderen Thread fortgesetzt.
Die IsyFact setzt grundsätzlich auf dem imperativen Programmiermodell auf und nutzt ThreadLocal-Mechanismen zur Propagierung von Kontext-Informationen.
Diese Mechanismen funktionieren im reaktiven Modell nicht zuverlässig, da ThreadLocal-Werte beim Threadwechsel auf dem neuen Thread nicht zur Verfügung stehen.
|
Reaktive Verarbeitung wird durch die IsyFact aktuell nicht vollständig unterstützt.
Fachverfahren, die den |
4.1. Einschränkungen bei isy-logging
Der Baustein isy-logging basiert auf dem ThreadLocal-basierten Mapped Diagnostic Context (MDC) von Logback.
Der MDC speichert Kontextinformationen wie die Korrelations-ID und die System-ID individuell pro Thread.
Beim Einsatz des WebClient wechselt Project Reactor bei asynchronen Operationen den ausführenden Thread.
Die MDC-Map des neuen Threads ist leer oder enthält Werte eines anderen Requests, der zufällig auf demselben Thread verarbeitet wird.
Dies hat Konsequenzen:
-
Die Korrelations-ID und alle weiteren MDC-Felder gehen bei Thread-Wechseln verloren oder enthalten falsche Werte.
-
Dieser Datenverlust tritt ohne Fehler oder Exception auf und ist im Logging nicht erkennbar.
-
Die Logging-Nachvollziehbarkeit über Komponentengrenzen hinweg ist also nicht gewährleistet.
Darüber hinaus basiert der LogInterceptor des Bausteins isy-logging auf Spring AOP.
Spring-AOP-Aspekte sind an den aufrufenden Thread gebunden und können reaktive Pipelines daher nur eingeschränkt instrumentieren.
4.2. Einschränkungen bei isy-security
Der Baustein isy-security nutzt den SecurityContextHolder von Spring Security.
Dieser speichert den SecurityContext in einem ThreadLocal.
In diesem sind Authentifizierungsinformationen, zugewiesene Rollen und Behördenkennzeichen enthalten.
Bei reaktiver Verarbeitung ist der SecurityContext nach einem Thread-Wechsel auf dem neuen Thread nicht mehr verfügbar.
Dies hat folgende Auswirkungen:
-
Wird eine durch
isy-securityabgesicherte Methode nach einem Thread-Wechsel aufgerufen, schlägt die Autorisierung mit einerOAuth2AuthenticationExceptionfehl. -
Werden keine abgesicherten Methoden aufgerufen, verhalten sich diese wie für einen anonymen Aufrufer. Das Fehlen des
SecurityContextfällt in diesem Fall nicht auf.
Die Propagierung des SecurityContext in reaktiven Pipelines über den ReactiveSecurityContextHolder von Spring Security ist aktuell nicht Bestandteil der IsyFact.
4.3. Einschränkungen bei isy-task
Der Baustein isy-task integriert isy-logging und isy-security in das Spring Scheduling.
Wird innerhalb von Tasks reaktiver Code ausgeführt z.B. durch Nutzung des WebClient – werden die oben beschriebenen Einschränkungen von isy-logging und isy-security geerbt.
Zusätzlich ist die Task-Überwachung von isy-task für den imperativen Ausführungsfall konzipiert.
Im reaktiven Modell endet die gemessene Ausführungsdauer eines Tasks bereits beim Aufruf von subscribe() und nicht erst nach Abschluss der reaktiven Pipeline.
Die tatsächliche Laufzeit eines reaktiven Tasks wird damit nicht korrekt gemessen.
4.4. Architekturelle Einordnung
Die fehlende Unterstützung reaktiver Verarbeitung durch die IsyFact stellt eine bekannte architekturelle Lücke dar.
Wird der WebClient eingesetzt, können die beschriebenen Probleme nicht eigenständig behoben werden:
Eigene AOP-Aspekte sind gemäß den Vorgaben der IsyFact nicht zulässig, und aktuell werden keine reaktiven Alternativen bereitgestellt.
Technische Lösungsansätze existieren grundsätzlich:
-
Project Reactor unterstützt in Kombination mit Micrometer die automatische Propagierung von Kontext-Informationen in reaktiven Pipelines (
Hooks.enableAutomaticContextPropagation()). Darüber ließe sich die MDC-Propagierung vonisy-loggingreaktiv-kompatibel gestalten. -
Spring Security stellt mit dem
ReactiveSecurityContextHoldereine reaktive Alternative zumSecurityContextHolderbereit, die denSecurityContextim Reactor-Kontext verwaltet. -
Der AOP-Aspekt der Task-Überwachung in
isy-taskmüsste für reaktive Rückgabetypen angepasst werden, sodass der Kontext erst nach Abschluss der reaktiven Pipeline bereinigt wird.
Diese Lösungsansätze sind aktuell nicht Bestandteil der IsyFact.
5. Schnittstellendokumentation
Alle auf IsyFact basierten Anwendungen müssen ihre REST-Services mit der OpenAPI 3.0 Spezifikation beschreiben. Sowohl YAML als auch JSON sind als Format der Schnittstellendokumentation zulässig. Die IsyFact empfiehlt zur Erstellung der Schnittstellendokumentation den OpenAPI (Swagger) Editor.
5.1. Code-Generierung
Zur 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 und sieht hierzu die Erstellung einer OpenAPI-Spezifikation vor.
Für die Generierung von Code empfiehlt die IsyFact den OpenAPI Generator.
5.2. Bereitstellen von generierten Clients
Neben dem Server können aus einer OpenAPI-Spezifikation auch Clients für diverse Technologie-Stacks generiert werden. Aus Convenience-Gründen wird empfohlen, dass Anbieter von Services fertig generierte Clients für die gängigen Technologie-Stacks zur Verfügung stellen: Java (Spring) und TypeScript (Angular).
6. Verwendung von Transportobjekten
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.
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
# [...]
components:
schemas:
Pet:
name:
type: string
example: doggie