Spring 6: ProblemDetail die Standardisierung von Fehlern in REST APIs

Der RFC 7807 standardisiert das Format von Fehlern in HTTP Responses.
Spring 6 liefert die Umsetzung mit der Klasse ProblemDetail!

In der täglichen Arbeit nutzen wir viele Services über ihre REST APIs. Ein klassisches Problem ist, dass verschiedene APIs die gleichen Datenmodelle auf unterschiedliche Weise modellieren. Dann sind wir als Nutzer dieser APIs genötigt Mapper zwischen den API-Modellen zu schreiben.

Dieses Problem gibt es auch bei Fehlern, welche beim Nutzen einer API auftreten können. Viele APIs verwenden hier unterschiedliche Formate. Der RFC 7807 standardisiert das Fehler-Format, siehe:
https://www.rfc-editor.org/rfc/rfc7807.html 

Ein sinnvolles Feature von Spring 6 ist die Umsetzung des RFC 7807 durch die Klasse ProblemDetail. Im Folgenden zeige ich euch wie einfach ihr das in eurer API nutzen könnt. Dann bekommt ihr standardisierte Fehlerbeschreibungen wie diese:

{
    "type""about:blank",
    "title""Item not found.",
    "status"404,
    "detail""Item with id 3 was not found.",
    "instance""/item"
}

ProblemDetail als Teil der ResponseEntity

import org.springframework.http.ProblemDetail;
...
@RestController
public class ItemController {

    private final List<Item> items = new ArrayList<>();
    @GetMapping("/item")
    public ResponseEntity getItem(int id) {
        var optItem = items.stream()
                .filter(item -> item.id == id).findFirst();
        if (optItem.isPresent())
            return ResponseEntity.ok(optItem.get());
ProblemDetail pd = ProblemDetail.forStatus(404);
pd.setTitle("Item not found.");
pd.setDetail("Item with id %d was not found.".formatted(id));
return ResponseEntity.status(404).body(pd);
    }
  • Der hier gezeigte @RestController und die Methode getItem sind ohne besondere Überraschungen aufgebaut. Die Grundlagen habe ich in diesem Blog-Artikel beschrieben.
  • Mit Spring 6 ist die Fehlerbehandlung mittels ProblemDetail neu bzw. folgt nun dem RFC 7807 Standard. ProblemDetail ist eine Datenklasse mit den 5 standardisierten Attributen zur Beschreibung eines Fehlers:
    • title - Zusammenfassung des Fehlers
    • detail - Detaillierte Fehlerbeschreibung
    • status - HTTP Status Code
    • type - Link zu einer detaillierten Erklärung des Problems
      (vermutlich meistens nicht gesetzt)
    • instance - URI Referenz zum genauen Ort, wo der Fehler auftrat 
  • ProblemDetail kann bei Bedarf noch weitere nicht standardisierte Attribute aufnehmen, um Domänen-spezifische Fehler genauer zu beschreiben. Damit verlassen wir zwar den Pfad der Standardisierung, aber zumindest die zuvor genannten 5 Attribute sind weiterhin standardisiert.
  • Analog zum Item im Erfolgsfall wird das ProblemDetail einfach im Body der ResponseEntity übertragen.

ProblemDetail als Standard im Exception-Handling

Sollte es in eurer REST-API zu unerwarteten Fehlern kommen, könnt ihr diese in globalen Exception Handlern fangen und verarbeiten. Die Verarbeitung erfolgt dank Spring 6 im standardisierten Format von ProblemDetail.

@ControllerAdvice
public class ExceptionHandler extends ResponseEntityExceptionHandler{
    
    @ExceptionHandler(IllegalArgumentException.class)
    public ProblemDetail handle(IllegalArgumentException ex){
ProblemDetail body = ProblemDetail.forStatusAndDetail(
                HttpStatusCode.valueOf(400),
ex.getLocalizedMessage());
        body.setTitle("Invalid id");
return body;
    }
}
  • Das ProblemDetail wird wieder analog zum vorherigen Kapitel erzeugt. Leider gibt es in Spring 6.0.0 keinen Builder zum Erstellen von ProblemDetail Instanzen. Daher müssen klassische Setter-Methoden verwendet werden.
  • @ControllerAdivce und @ExceptionHandler sind die Techniken zur Fehlerbehandlung mit Spring an zentraler Stelle. Das möchte ich hier nicht weiter erklären, da es kein neues Spring 6 Feature ist.

Kurzes Fazit

ProblemDetail ist ein kleines Feature, welches ein echtes Problem durch Standardisierung löst. Da es aber nur ein kleines Feature ist, gibt es hier auch nur ein kurzes Fazit ;-)

Kommentare

Beliebte Posts aus diesem Blog

OpenID Connect mit Spring Boot 3

Authentifizierung in Web-Anwendungen mit Spring Security 6

Reaktive REST-Webservices mit Spring WebFlux