Explain what DefaultHandlerExceptionResolver and ResponseStatusExceptionResolver do, including the body/status subtleties.
answer
- DHER: framework exceptions → status codes (405/415/406/400/404)
- sets status + headers, empty ModelAndView, no custom body
- RSER: @ResponseStatus OR ResponseStatusException
- reason present → sendError → /error page
- Spring 6: ErrorResponse + ProblemDetail (rendered by EHER)
basics
~20 sDefaultHandlerExceptionResolver maps Spring MVC's built-in exceptions (like 405, 415, 400) to status codes. ResponseStatusExceptionResolver applies the status from @ResponseStatus-annotated exceptions or a ResponseStatusException. Both mainly set the status; they don't produce a custom response body.
solid answer
~40 sDefaultHandlerExceptionResolver is the framework's fallback: it recognizes Spring MVC's own exceptions and translates them to HTTP statuses — HttpRequestMethodNotSupportedException→405 (with an Allow header), HttpMediaTypeNotSupportedException→415, HttpMediaTypeNotAcceptableException→406, MissingServletRequestParameterException/MethodArgumentNotValidException→400, NoHandlerFoundException→404, MissingPathVariableException→500, etc. ResponseStatusExceptionResolver handles @ResponseStatus-annotated exception classes and ResponseStatusException instances, applying their status/reason. The subtlety: when a reason is present, ResponseStatusExceptionResolver calls response.sendError(status, reason), which commits an error and delegates body generation to the container's error page (e.g., /error in Boot); with no reason it uses setStatus. Neither resolver writes your own JSON body — DefaultHandlerExceptionResolver only sets status (Boot's /error fills the body). To control the body, handle the exception with @ExceptionHandler or subclass ResponseEntityExceptionHandler. In Spring 6 these framework exceptions implement ErrorResponse and carry a ProblemDetail.
code
java · 20 lines// ResponseStatusExceptionResolver handles BOTH of these:
// (a) annotation on the exception type
@ResponseStatus(HttpStatus.NOT_FOUND) // NOTE: no 'reason' -> setStatus, your body kept
class OrderNotFoundException extends RuntimeException {
OrderNotFoundException(String id) { super("order " + id); }
}
// (b) thrown inline, status decided per call site
@GetMapping("/orders/{id}")
Order get(@PathVariable String id) {
return repo.find(id)
.orElseThrow(() -> new ResponseStatusException(
HttpStatus.NOT_FOUND, "order " + id + " not found"));
// reason present -> sendError -> container /error page in Spring Boot
}
// DefaultHandlerExceptionResolver fires automatically, no code:
// POST body that fails JSON parsing -> HttpMessageNotReadableException -> 400
// wrong Content-Type -> HttpMediaTypeNotSupportedException -> 415go deeper
Know DefaultHandlerExceptionResolver gives correct status codes for framework errors automatically.
Recall the key mappings and that ResponseStatusExceptionResolver handles @ResponseStatus and ResponseStatusException.
Explain the sendError-vs-setStatus body implication and how to override the body via @ExceptionHandler.
Connect to Spring 6 ErrorResponse/ProblemDetail and ResponseEntityExceptionHandler, and reason about content negotiation on error responses.
## DefaultHandlerExceptionResolver — the framework's safety net This resolver exists so Spring MVC's *own* exceptions map to correct status codes even if you write no error handling. It is essentially a big type dispatch (`doResolveException` → `handleXxx` methods). Key mappings: | Exception (thrown by Spring MVC internals) | Status | Notes | |---|---|---| | `HttpRequestMethodNotSupportedException` | 405 | sets `Allow` header | | `HttpMediaTypeNotSupportedException` | 415 | sets `Accept` header | | `HttpMediaTypeNotAcceptableException` | 406 | | | `MissingPathVariableException` | 500 | server bug, not client | | `MissingServletRequestParameterException` | 400 | | | `MissingServletRequestPartException` | 400 | | | `ServletRequestBindingException` | 400 | | | `MethodArgumentNotValidException` | 400 | @Valid body failure | | `HandlerMethodValidationException` | 400 | (Spring 6.1) | | `NoHandlerFoundException` | 404 | needs throwExceptionIfNoHandlerFound | | `NoResourceFoundException` | 404 | (Spring 6.1 static resources) | | `ConversionNotSupportedException` | 500 | | | `TypeMismatchException` | 400 | | | `HttpMessageNotReadableException` | 400 | malformed body | | `HttpMessageNotWritableException` | 500 | | | `AsyncRequestTimeoutException` | 503 | | It sets the status/headers and returns an **empty ModelAndView**. It does **not** write a JSON body describing the error — in Spring Boot the body then comes from the `/error` mapping. ## ResponseStatusExceptionResolver Two trigger mechanisms: 1. **`@ResponseStatus`** on the exception class: ```java @ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Order not found") class OrderNotFoundException extends RuntimeException {} ``` 2. **`ResponseStatusException`** (Spring 5+), status passed at throw time — no annotation needed, so you can vary the status per call site: ```java throw new ResponseStatusException(HttpStatus.CONFLICT, "already exists"); ``` It reads the status (and optional reason), applies it, and returns an empty ModelAndView. ### The sendError vs setStatus subtlety - If a **reason** is supplied, the resolver calls `response.sendError(statusCode, reason)`. `sendError` commits the response and hands off to the container's error mechanism — in Boot that is an ERROR dispatch to `/error`, so the reason may appear in the error page's `message` field, and your own controller body is discarded. - If **no reason**, it calls `response.setStatus(statusCode)` and the response stays under your control. This is why `@ResponseStatus(reason="...")` sometimes produces a different body shape than you expect: it routes through the container error page. ## Spring 6 / ProblemDetail In Spring Framework 6, most of the DefaultHandlerExceptionResolver exceptions implement **`ErrorResponse`** (many extend `ErrorResponseException`) and expose a **`ProblemDetail`** (RFC 7807/9457). Combined with `ResponseEntityExceptionHandler` (a `@ControllerAdvice` base class) they can render structured `application/problem+json` bodies. But that rendering happens through `ExceptionHandlerExceptionResolver`, not DefaultHandlerExceptionResolver — the latter still just sets status. ## When to use / override - Rely on DefaultHandlerExceptionResolver for correct **status codes** for free. - To also control the **body** of those framework errors, add `@ExceptionHandler` methods (which run first via ExceptionHandlerExceptionResolver) or extend `ResponseEntityExceptionHandler` and override the relevant `handleXxx`. - Use `ResponseStatusException` for quick, per-call status without a bespoke exception class; use `@ResponseStatus` when the exception type always maps to one status. ## Gotchas - `@ResponseStatus(reason=...)` → container error page (sendError). Omit reason to keep your body. - DefaultHandlerExceptionResolver gives status but no descriptive body by itself. - `NoHandlerFoundException` only fires if configured to throw; otherwise 404 is produced without this resolver.
- Why might @ResponseStatus(reason="...") produce a different response body than you set in a @ResponseBody method?Because with a reason present, ResponseStatusExceptionResolver calls response.sendError, which commits and delegates to the container's error page (/error in Boot). Your controller body is discarded and the error page renders instead.
- DefaultHandlerExceptionResolver returns a 400 but an empty/default body. How do you add a structured problem+json body for that same exception?Handle it earlier in the chain: write an @ExceptionHandler (or override the matching handleXxx in a ResponseEntityExceptionHandler subclass). ExceptionHandlerExceptionResolver runs first and can return a ProblemDetail/ResponseEntity body.
saying these in an interview costs you the question
- Claiming DefaultHandlerExceptionResolver writes a descriptive JSON body itself
- Thinking ResponseStatusException needs the @ResponseStatus annotation
- Not knowing that a reason triggers sendError and the container error page