Three companion modules verifying and rewriting the Boot 4.1.1 / Framework 7.0.9 story for three older articles: the javax->jakarta.validation namespace fix plus Jakarta Validation 3.1 record-validation clarification, ETag/ conditional-request APIs re-verified unchanged plus the starter rename, and RestTemplate Basic Auth rebuilt on RestClient with the exchange() trap called out. 19 real passing tests generate every transcript quoted from the three companion articles. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01EQNA6DJ9VgCtW6zhCE8Xud
4.5 KiB
3. What the defaults do not do: record validation failures with no error body
Prev: Record validation | README
Source: ContactController.java.
Test: ValidationScenariosTest.java,
ProblemDetailsEnabledTest.java.
Transcripts: docs/output/03-record-based-spam-rejected.txt,
docs/output/03b-record-based-spam-rejected-accept-json.txt,
docs/output/07-problemdetails-enabled-record-rejected.txt.
The surprise this chapter exists to document
ContactController#submitContactForm (the class-based endpoint) declares a BindingResult
parameter immediately after @Valid ContactForm. That is what lets it inspect
bindingResult.getFieldErrors() and hand back a {"message": "..."} body of its own construction.
A record parameter has nowhere convenient to put an equivalent BindingResult in this codebase's
controller signatures, so submitContactFormRecord has none -- and a failing constraint on the
record path throws MethodArgumentNotValidException instead of populating a result object.
The assumption going in was that Spring's default handling of that exception would still produce some readable body. It does not, by default:
$ curl -s -i -X POST localhost:8080/contact-record \
-H 'Content-Type: application/json' \
-d '{"email":"[email protected]","message":"This is spam."}'
HTTP status: 400
Body: '' (empty!)
(docs/output/03-record-based-spam-rejected.txt)
An Accept: application/json header does not change this. It was tested
directly rather than assumed -- see
docs/output/03b-record-based-spam-rejected-accept-json.txt.
The empty body is not a content-negotiation problem; nothing is being negotiated because nothing is
being written.
The property that changes this, and what it still does not give you
spring.mvc.problemdetails.enabled=true (a property that already existed in Spring Boot 3, not
new in 4.1) turns on RFC 9457 ProblemDetail responses for framework-thrown MVC exceptions,
MethodArgumentNotValidException included:
HTTP status: 400
Content-Type: application/problem+json
Body: {"detail":"Invalid request content.","instance":"/contact-record","status":400,"title":"Bad Request"}
(docs/output/07-problemdetails-enabled-record-rejected.txt)
Progress -- there is now a body, and a real HTTP status-coded RFC 9457 document -- but read it
closely: no mention of "spam", no field name, no constraint message. Boot's default mapping fills
in only the generic fields (title, status, a fixed detail string). The per-field detail the
class-based endpoint hand-rolls from bindingResult.getFieldErrors() is not reproduced
automatically; getting it back needs a custom @ExceptionHandler (or a
ResponseEntityExceptionHandler override of handleMethodArgumentNotValid) that reads the
exception's BindingResult -- the exception itself still carries one, even though the controller
signature does not expose it -- and writes the field errors into the ProblemDetail's own
properties map.
Turning onspring.mvc.problemdetails.enabledis not, by itself, a drop-in replacement for theBindingResult-based error reporting pattern most Spring tutorials (including the earlier version of this one) teach. It replaces an empty body with a standardised envelope; it does not replace the work of putting your validation messages inside that envelope.
Going deeper
- Spring Framework
ProblemDetailreference (rel="nofollow") - RFC 9457: Problem Details for HTTP APIs (rel="nofollow")
- See also this repo's
problem-details/module, built for a different ankurm.com article specifically aboutProblemDetailand global exception handling in depth. - Prev: Record validation