# custom-validation Companion module for [**Custom Validation in Spring Boot: Beyond the Basics!**](https://ankurm.com/custom-validation-in-spring-boot-beyond-the-basics/) on ankurm.com, rewritten around **Jakarta Validation 3.1** (Spring Boot 4.1.1 / Hibernate Validator 9.1.3.Final) and the `jakarta.validation` namespace the original article's `javax.validation` code never actually ran under on Boot 3+. `mvn test` regenerates every transcript in [`docs/output/`](docs/output) -- the test suite is the transcript generator. ## Versions | | | |---|---| | Spring Boot | 4.1.1 | | Spring Framework | 7.0.9 | | Hibernate Validator | 9.1.3.Final (`jakarta.validation-api` 3.1.1) -- confirmed with `mvn dependency:tree` and by reading the jars' own manifests, see [docs/01](docs/01-jakarta-namespace-and-bean-validation-3-1.md) | | JDK | Eclipse Temurin 25.0.4.1 (LTS) | ## Quickstart ```bash export JAVA_HOME=/path/to/jdk-25 mvn -DskipTests package ./scripts/run.sh # port 8080 curl -s -X POST localhost:8080/contact -H 'Content-Type: application/json' \ -d '{"email":"user@example.com","message":"hello there, this message is long enough"}' mvn test # 10 tests, regenerates docs/output/ ``` ## Endpoints | Endpoint | Shows | |---|---| | `POST /contact` | field-level custom constraint (`@SpamMessageCheck`), class-based DTO, `BindingResult` | | `POST /contact-record` | the same constraint on a record component | | `POST /booking` | class-level (cross-field) custom constraint (`@DateRangeValid`), class-based DTO | | `POST /booking-record` | the same cross-field constraint on a record's type declaration | | `GET /diagnostic/validation-provider` | prints the real Bean Validation provider and spec version at runtime -- delete before shipping | ## Documentation 1. [The jakarta.validation namespace, and what Bean Validation 3.1 actually changed](docs/01-jakarta-namespace-and-bean-validation-3-1.md) 2. [Record validation, field-level and class-level](docs/02-record-validation.md) 3. [What the defaults do not do: record validation failures with no error body](docs/03-what-the-defaults-do-not-do.md) ## Findings worth the trip - **The original article's `javax.validation.*` imports never worked on Spring Boot 3+.** Boot 3 moved entirely to the `jakarta.*` namespace in December 2022; this module is the corrected, runnable version. - **Spring Boot 4.1.1 pins Hibernate Validator 9.1.3.Final / Jakarta Validation 3.1**, confirmed by `mvn dependency:tree` and independently by reading the actual jar manifests at test time. - **A record validation failure with no `BindingResult` in the controller signature returns an EMPTY 400 body by default** -- not a JSON error, not even with `Accept: application/json`. - **`spring.mvc.problemdetails.enabled=true` fixes the empty body but not the missing detail**: the resulting `ProblemDetail` is generic (`"Invalid request content."`), with no per-field messages, unless you write a custom `@ExceptionHandler` to put them there yourself. - **`AutoConfigureMockMvc` moved packages in Boot 4.1**, from `org.springframework.boot.test.autoconfigure.web.servlet` to `org.springframework.boot.webmvc.test.autoconfigure` -- and `spring-boot-starter-test` alone no longer pulls in MockMvc's autoconfiguration; that needs `spring-boot-starter-webmvc-test`. ## License MIT -- see [LICENSE](../LICENSE).