Querschnittliche Aspekte

1. Validierung von Anfragen

Die formale Validierung der Daten muss 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.

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.

2.1. Entkopplung von Fehlerbehandlung und Service-Logik

Die Fehlerbehandlung erfolgt wie im Detailkonzept Service beschrieben, außerhalb der Service-Fassade, an zentraler Stelle, in einer Exception-Fassade. Die Exception-Fassade wird durch RestExceptionHandler Klassen implementiert, (vgl. Entkopplung Fehlerbehandlung und Service-Logik). In der Exception-Fassade werden alle Exceptions aus Methoden des Anwendungskerns oder Methoden der Service-Endpoints gefangen, geloggt, in Exception-Transport-Objekte umgewandelt und als Response zurückgegeben.

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

Spring Webflux unterstützt dazu ebenso wie Spring MVC die Annotation @RestControllerAdvice. Die Implementierung der RestExceptionHandler-Funktionalität kann daher über die Annotation einer Klasse mit @RestControllerAdvice erfolgen.

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

2.2. Einheitliche Fehlermeldungen

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

Tabelle 1. 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 gemappt. Welche Informationen enthalten sein dürfen, wird in den Nutzungsvorgaben Fehlerbehandlung genauer beschrieben.

3. Transaktionssteuerung

In der Regel geschieht die Transaktionssteuerung im Anwendungskern. Die Ausnahme bilden Anforderungen, aus denen heraus die Service-Fassade einer Fachkomponente der Serviceschicht eine Transaktion über mehrere Aufrufe des Anwendungskerns hinweg bilden muss. In diesem Fall fällt die Steuerung der Transaktion der Service-Fassade zu, weil es wichtig ist, dass die Fehlerbehandlung in jedem Fall die Transaktion umschließt. Nur so ist gewährleistet, dass auch Fehler, die beim Commit bzw. Rollback entstehen, von der Fehlerbehandlung erfasst werden.

Die entsprechenden Service-Operationen werden über die Annotation @Transactional an den Methoden der Service-Fassade konfiguriert.

Eine Sonderstellung nehmen Services ein, die im Fehlerfall keinen Fehler zurückgeben, sondern die Fehler in der Antwortnachricht übermitteln. Der AOP-Transaktionsmanager würde kein Rollback durchführen, da alle Exceptions abgefangen werden, auf die er reagieren könnte. Um auch in diesem Fall ein Rollback der Transaktion zu erzwingen, ist folgender Aufruf durchzuführen:

Listing 1. Rollback von Transaktionen im Fehlerfall ohne Exceptions
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

4. 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 OAuth2 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 Security zu beachten.

5. Logging

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

5.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.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.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 2. 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 inklusive dem kompletten Header geloggt werden. Das Aktivieren des Nachrichten-Logging ist über einen Konfigurationsparameter steuerbar.

5.4. Setzen der Korrelations-ID in den HTTP-Header

Beim Versenden einer REST-Anfrage ist der Header X-Correlation-Id mit dem Wert der Korrelations-ID zu setzen. Die Anwendung, welche die REST-Anfragen stellt, muss dieses Verhalten implementieren. In diesem Abschnitt wird ein Beispiel für die Implementierung gegeben.

Für das Setzen des korrekten Werts in den HTTP-Header sind folgende Schritte auszuführen.

Listing 2. Beispielimplementierung des Interfaces ExchangeFilterFunction
ExchangeFilterFunction addCorrelationId =
        (clientRequest, nextFilter) -> {

    StringBuilder correlationIdBuilder = new StringBuilder();

    String existingCorrelationId = MdcHelper.liesKorrelationsId(); (1)
    if (existingCorrelationId != null && !existingCorrelationId.isEmpty()) {
        correlationIdBuilder.append(existingCorrelationId).append(";");
    }

    String newCorrelationId = UUID.randomUUID().toString(); (2)
    correlationIdBuilder.append(newCorrelationId);
    MdcHelper.pushKorrelationsId(newCorrelationId);

    ClientRequest filteredRequest = ClientRequest.from(clientRequest)
        .header("X-Correlation-Id", correlationIdBuilder.toString()) (3)
        .build();
    return nextFilter.exchange(filteredRequest);
};
1 Auslesen der bestehenden Korrelations-ID aus dem MdcHelper.
2 Erzeugen einer neuen UUID, Anhängen an die bestehende Korrelations-ID und Setzen im MdcHelper.
3 Setzen der aktualisierten Korrelations-ID im Header X-Correlation-Id der REST-Anfrage.
Listing 3. Beispielkonfiguration des WebClient-Beans
@Bean
public WebClient webClient() {
    return WebClient.builder()
        .filter(addCorrelationId)
        .build();
}

Die ExchangeFilterFunction fügt Korrelations-IDs zu ausgehenden REST-Anfragen hinzu, die durch eingehende Requests, Tasks oder Batches ausgelöst werden. Nach abgeschlossener Bearbeitung werden die Korrelations-IDs durch den MdcHelper und in den Klassen HttpHeaderNestedDiagnosticContextFilter, IsyTaskAspect und BatchrahmenImpl entfernt.

Für weitere Details zu Korrelations-IDs siehe auch die entsprechenden Abschnitte im Konzept und den Nutzungsvorgaben des Bausteins Logging.

6. Ü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.

7. Versionierung

Versionierung kann auf verschiedene Weisen stattfinden.

Prinzipiell gilt es inkompatible API-Änderungen in einem REST-Service 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}