Companion code for "Global Exception Handling with ProblemDetail (RFC 9457) in Spring Boot 4". Thirteen failures under five handling setups (Boot defaults, the Boot flag, a ResponseEntityExceptionHandler advice, advice plus an ErrorController, a catch-all ordered first), validation errors, i18n, content negotiation, Security's 401/403, silent 500s and decoding on the client. 16 tests pin the behaviour. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01C3TETMrqVUWeFkNtz3Jbo3
2.7 KiB
1. The mental model: RFC 9457 and Spring's four types
Index · Next: 2. Choosing a mechanism →
What RFC 9457 actually specifies
A problem document is a JSON (or XML) object served as application/problem+json
(application/problem+xml) with five optional members:
| Member | Meaning |
|---|---|
type |
a URI identifying the kind of problem. When absent it is assumed to be about:blank |
title |
a short, human-readable summary of that kind - the same for every occurrence |
status |
the HTTP status, repeated for convenience |
detail |
a human-readable explanation of this occurrence |
instance |
a URI identifying this occurrence |
Anything else is an extension member - orderId, errors, errorId in this project.
RFC 9457 obsoletes RFC 7807. The wire format did not change. What it added: a registry of common
problem type URIs, guidance on multiple problems ("the most relevant or urgent problem" should be
represented), and guidance for type URIs that cannot be dereferenced. The XML namespace is still
urn:ietf:rfc:7807 - you will see it in content-negotiation.txt.
Spring's four types
| Type | What it is | Used here |
|---|---|---|
ProblemDetail |
the body: the five members plus a properties map for extensions |
every handler |
ErrorResponse |
an interface: "I know my status, headers and ProblemDetail" |
all Spring MVC exceptions implement it |
ErrorResponseException |
a convenient base class implementing ErrorResponse |
OutOfStockException |
ResponseEntityExceptionHandler |
a @ControllerAdvice base class that renders every Spring MVC exception as a problem |
GlobalExceptionHandler |
Who renders what
There are three places an error body can come from, and most confusion comes from not knowing which one produced the response you are looking at:
- An
@ExceptionHandler- inside theDispatcherServlet, for exceptions thrown by handler methods (and Spring MVC's own exceptions). - The container's error page - Tomcat forwards to
/error, Spring Boot'sBasicErrorControllerrenders{"timestamp","status","error","path"}. Everything the first place did not handle ends up here, including exceptions from servlet filters. - Code that writes the response directly - Spring Security's entry point and access-denied handler, or your own filters.
The five setups in chapter 2 differ only in which of these three
places produce application/problem+json.