Ausnahmebehandlung & ProblemDetail
@ExceptionHandler, @ControllerAdvice und standardisierte Fehlerantworten nach RFC 9457 (ProblemDetail).
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:
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.
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.
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.