Loslegen
Javaneer
Zurück zur Stufe
Modul 2·Web-APIs bauen

Ausnahmebehandlung & ProblemDetail

@ExceptionHandler, @ControllerAdvice und standardisierte Fehlerantworten nach RFC 9457 (ProblemDetail).

16 Min. LesezeitFortgeschritten

Deutsche Übersetzung in Arbeit

Diese Lektion ist noch nicht ins Deutsche übersetzt und wird daher auf Englisch angezeigt. Der Rest der Seite ist vollständig lokalisiert.

Auf dieser Seite

When something goes wrong - a book isn't found, validation fails, a duplicate is created - your API must respond with the right status code and a useful, consistent error body. Scattering try/catch through every controller is the wrong way. Spring gives you a clean, centralized approach built around a web standard.

ProblemDetail: a standard error shape

Ad-hoc error JSON ({"error": "not found"} here, {"message": "..."} there) forces every client to guess. ProblemDetail (RFC 9457) is a standard structure Spring supports out of the box:

Output
HTTP/1.1 404 Not Found
Content-Type: application/problem+json

{
"type": "https://bookvault.dev/errors/book-not-found",
"title": "Book not found",
"status": 404,
"detail": "No book found with ISBN 000-0000000000",
"instance": "/api/books/000-0000000000"
}

Every error in your API can share this shape, so clients parse failures the same way every time.

Centralize with @RestControllerAdvice

A class annotated @RestControllerAdvice holds @ExceptionHandler methods that apply across all controllers. One place maps each exception to a response:

@RestControllerAdvice
class ApiExceptionHandler {

    @ExceptionHandler(BookNotFoundException.class)
    ProblemDetail handleNotFound(BookNotFoundException ex) {
        ProblemDetail problem = ProblemDetail.forStatusAndDetail(
            HttpStatus.NOT_FOUND, ex.getMessage());
        problem.setTitle("Book not found");
        return problem;
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    ProblemDetail handleValidation(MethodArgumentNotValidException ex) {
        ProblemDetail problem = ProblemDetail.forStatus(HttpStatus.BAD_REQUEST);
        problem.setTitle("Validation failed");
        problem.setProperty("errors", ex.getBindingResult().getFieldErrors().stream()
            .map(e -> e.getField() + ": " + e.getDefaultMessage()).toList());
        return problem;
    }
}

Your controllers now stay clean - they just throw meaningful exceptions (throw new BookNotFoundException(isbn)), and the advice turns each into a proper response.

A central complaints desk

Imagine every department in a company handling customer complaints its own way - inconsistent, chaotic. A central complaints desk takes every issue, categorizes it, and responds in one consistent format. @RestControllerAdvice is that desk: all exceptions funnel to it, and it produces uniform, well-formed error responses.

Extend the ready-made base class

For common Spring MVC exceptions, extend ResponseEntityExceptionHandler - it already maps things like unreadable bodies and validation errors to ProblemDetail. Override only what you want to customize, and add handlers for your own exceptions.

Don't leak internals

A final rule: never return raw stack traces or exception messages from unexpected errors. Map unknown exceptions to a generic 500 ProblemDetail (log the details server-side), so you never expose internals to clients.

Two errors, one shape

BookVault must return 404 when a book isn't found and 400 when a create request fails validation - both as ProblemDetail bodies. Where do you put the mapping so no controller needs a try/catch, and what does each controller method do to trigger it?

What is the purpose of ProblemDetail (RFC 9457) in a Spring API?

Key takeaways

  • Handle errors centrally with a @RestControllerAdvice class of @ExceptionHandler methods - not try/catch in every controller.
  • Return ProblemDetail (RFC 9457): a standard type/title/status/detail structure, so clients parse all errors the same way.
  • Controllers stay clean and just throw meaningful exceptions (e.g. BookNotFoundException); the advice maps each to a response.
  • Extend ResponseEntityExceptionHandler to reuse Spring's built-in ProblemDetail mappings.
  • Never leak stack traces or internal messages - map unexpected errors to a generic 500 and log details server-side.
War diese Lektion hilfreich?
Diese Seite auf GitHub bearbeiten