Files
asmhatreandClaude Opus 5 926250e1a9 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
2026-09-11 17:12:22 +00:00

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`.