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
49 lines
2.7 KiB
Markdown
49 lines
2.7 KiB
Markdown
# 1. The mental model: RFC 9457 and Spring's four types
|
|
|
|
[Index](../README.md) · Next: [2. Choosing a mechanism →](02-choosing-a-mechanism.md)
|
|
|
|
## 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`](output/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`](../src/main/java/com/ankurm/problems/domain/OutOfStockException.java) |
|
|
| `ResponseEntityExceptionHandler` | a `@ControllerAdvice` base class that renders every Spring MVC exception as a problem | [`GlobalExceptionHandler`](../src/main/java/com/ankurm/problems/advice/GlobalExceptionHandler.java) |
|
|
|
|
## 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:
|
|
|
|
1. **An `@ExceptionHandler`** - inside the `DispatcherServlet`, for exceptions thrown by handler
|
|
methods (and Spring MVC's own exceptions).
|
|
2. **The container's error page** - Tomcat forwards to `/error`, Spring Boot's
|
|
`BasicErrorController` renders `{"timestamp","status","error","path"}`. Everything the first
|
|
place did not handle ends up here, including exceptions from servlet filters.
|
|
3. **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](02-choosing-a-mechanism.md) differ only in which of these three
|
|
places produce `application/problem+json`.
|