Add problem-details: global exception handling with RFC 9457
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
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
# 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`.
|
||||
Reference in New Issue
Block a user