Skip to content

Commit df2617d

Browse files
committed
Document method validation improvements
Closes gh-30643
1 parent a03a147 commit df2617d

File tree

13 files changed

+409
-69
lines changed

13 files changed

+409
-69
lines changed

framework-docs/modules/ROOT/nav.adoc

+2
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@
267267
***** xref:web/webmvc/mvc-controller/ann-methods/jackson.adoc[]
268268
**** xref:web/webmvc/mvc-controller/ann-modelattrib-methods.adoc[]
269269
**** xref:web/webmvc/mvc-controller/ann-initbinder.adoc[]
270+
**** xref:web/webmvc/mvc-controller/ann-validation.adoc[]
270271
**** xref:web/webmvc/mvc-controller/ann-exceptionhandler.adoc[]
271272
**** xref:web/webmvc/mvc-controller/ann-advice.adoc[]
272273
*** xref:web/webmvc-functional.adoc[]
@@ -361,6 +362,7 @@
361362
***** xref:web/webflux/controller/ann-methods/jackson.adoc[]
362363
**** xref:web/webflux/controller/ann-modelattrib-methods.adoc[]
363364
**** xref:web/webflux/controller/ann-initbinder.adoc[]
365+
**** xref:web/webflux/controller/ann-validation.adoc[]
364366
**** xref:web/webflux/controller/ann-exceptions.adoc[]
365367
**** xref:web/webflux/controller/ann-advice.adoc[]
366368
*** xref:web/webflux-functional.adoc[]

framework-docs/modules/ROOT/pages/core/validation/beanvalidation.adoc

+79-15
Original file line numberDiff line numberDiff line change
@@ -123,15 +123,12 @@ Validator, is expected to be present in the classpath and is automatically detec
123123

124124

125125
[[validation-beanvalidation-spring-inject]]
126-
=== Injecting a Validator
126+
=== Inject Jakarta Validator
127127

128128
`LocalValidatorFactoryBean` implements both `jakarta.validation.ValidatorFactory` and
129-
`jakarta.validation.Validator`, as well as Spring's `org.springframework.validation.Validator`.
130-
You can inject a reference to either of these interfaces into beans that need to invoke
131-
validation logic.
132-
133-
You can inject a reference to `jakarta.validation.Validator` if you prefer to work with the Bean
134-
Validation API directly, as the following example shows:
129+
`jakarta.validation.Validator`, so you can inject a reference to the latter to
130+
apply validation logic if you prefer to work with the Bean Validation API directly,
131+
as the following example shows:
135132

136133
[tabs]
137134
======
@@ -160,8 +157,15 @@ Kotlin::
160157
----
161158
======
162159

163-
You can inject a reference to `org.springframework.validation.Validator` if your bean
164-
requires the Spring Validation API, as the following example shows:
160+
161+
[[validation-beanvalidation-spring-inject-adapter]]
162+
=== Inject Spring Validator
163+
164+
In addition to implementing `jakarta.validation.Validator`, `LocalValidatorFactoryBean`
165+
also adapts to `org.springframework.validation.Validator`, so you can inject a reference
166+
to the latter if your bean requires the Spring Validation API.
167+
168+
For example:
165169

166170
[tabs]
167171
======
@@ -190,9 +194,15 @@ Kotlin::
190194
----
191195
======
192196

197+
When used as `org.springframework.validation.Validator`, `LocalValidatorFactoryBean`
198+
invokes the underlying `jakarta.validation.Validator`, and then adapts
199+
``ContraintViolation``s to ``FieldError``s, and registers them with the `Errors` object
200+
passed into the `validate` method.
201+
202+
193203

194204
[[validation-beanvalidation-spring-constraints]]
195-
=== Configuring Custom Constraints
205+
=== Configure Custom Constraints
196206

197207
Each bean validation constraint consists of two parts:
198208

@@ -274,9 +284,8 @@ As the preceding example shows, a `ConstraintValidator` implementation can have
274284
[[validation-beanvalidation-spring-method]]
275285
=== Spring-driven Method Validation
276286

277-
You can integrate the method validation feature supported by Bean Validation 1.1 (and, as
278-
a custom extension, also by Hibernate Validator 4.3) into a Spring context through a
279-
`MethodValidationPostProcessor` bean definition:
287+
You can integrate the method validation feature of Bean Validation into a
288+
Spring context through a `MethodValidationPostProcessor` bean definition:
280289

281290
[tabs]
282291
======
@@ -305,11 +314,11 @@ XML::
305314
----
306315
======
307316

308-
To be eligible for Spring-driven method validation, all target classes need to be annotated
317+
To be eligible for Spring-driven method validation, target classes need to be annotated
309318
with Spring's `@Validated` annotation, which can optionally also declare the validation
310319
groups to use. See
311320
{api-spring-framework}/validation/beanvalidation/MethodValidationPostProcessor.html[`MethodValidationPostProcessor`]
312-
for setup details with the Hibernate Validator and Bean Validation 1.1 providers.
321+
for setup details with the Hibernate Validator and Bean Validation providers.
313322

314323
[TIP]
315324
====
@@ -320,6 +329,61 @@ xref:core/aop/proxying.adoc#aop-understanding-aop-proxies[Understanding AOP Prox
320329
to always use methods and accessors on proxied classes; direct field access will not work.
321330
====
322331

332+
By default, `jakarta.validation.ConstraintViolationException` is raised with the set of
333+
``ConstraintViolation``s returned by `jakarata.validation.Validator`. As an alternative,
334+
you can have `MethodValidationException` raised instead with ``ConstraintViolation``s
335+
adapted to `MessageSourceResolvable` errors. To enable set the following flag:
336+
337+
[tabs]
338+
======
339+
Java::
340+
+
341+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
342+
----
343+
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
344+
345+
@Configuration
346+
public class AppConfig {
347+
348+
@Bean
349+
public MethodValidationPostProcessor validationPostProcessor() {
350+
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
351+
processor.setAdaptConstraintViolations(true);
352+
return processor;
353+
}
354+
}
355+
356+
----
357+
358+
XML::
359+
+
360+
[source,xml,indent=0,subs="verbatim,quotes",role="secondary"]
361+
----
362+
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
363+
<property name="adaptConstraintViolations" value="true"/>
364+
</bean>
365+
----
366+
======
367+
368+
`MethodValidationException` contains a list of ``ParameterValidationResult``s which
369+
group errors by method parameter, and each exposes a `MethodParameter`, the argument
370+
value, and a list of `MessageSourceResolvable` errors adapted from
371+
``ConstraintViolation``s. For `@Valid` method parameters with cascaded violations on
372+
fields and properties, the `ParameterValidationResult` is `ParameterErrors` which
373+
implements `org.springframework.validation.Errors` and exposes validation errors as
374+
``FieldError``s.
375+
376+
The adapted `MessageSourceResolvable` errors can be turned into error messages to
377+
display to users through the configured
378+
xref:core/beans/context-introduction.adoc#context-functionality-messagesource[`MessageSource`]
379+
based on locale and language specific resource bundles.
380+
381+
NOTE: Spring MVC and WebFlux have built-in support for method validation, and therefore
382+
for web controller methods there is no need for a class level `@Validated` and an AOP proxy.
383+
See the Spring MVC xref:web/webmvc/mvc-controller/ann-validation.adoc[Validation] section,
384+
the WebFlux xref:web/webflux/controller/ann-validation.adoc[Validation] section,
385+
and the xref:web/webmvc/mvc-controller/ann-validation.adoc[Error Responses] section.
386+
323387

324388

325389

framework-docs/modules/ROOT/pages/web/webflux/ann-rest-exceptions.adoc

+28-24
Original file line numberDiff line numberDiff line change
@@ -67,23 +67,23 @@ from an existing `ProblemDetail`. This could be done centrally, e.g. from an
6767

6868

6969
[[webflux-ann-rest-exceptions-i18n]]
70-
== Internationalization
70+
== Customization and i18n
7171
[.small]#xref:web/webmvc/mvc-ann-rest-exceptions.adoc#mvc-ann-rest-exceptions-i18n[See equivalent in the Servlet stack]#
7272

73-
It is a common requirement to internationalize error response details, and good practice
74-
to customize the problem details for Spring WebFlux exceptions. This section describes the
75-
support for that.
73+
It is a common requirement to customize and internationalize error response details.
74+
It is also good practice to customize the problem details for Spring WebFlux exceptions
75+
to avoid revealing implementation details. This section describes the support for that.
7676

77-
`ErrorResponse` exposes message codes for "type", "title", and "detail", in addition to
77+
An `ErrorResponse` exposes message codes for "type", "title", and "detail", as well as
7878
message code arguments for the "detail" field. `ResponseEntityExceptionHandler` resolves
7979
these through a xref:core/beans/context-introduction.adoc#context-functionality-messagesource[MessageSource]
80-
and updates the `ProblemDetail` accordingly.
80+
and updates the corresponding `ProblemDetail` fields accordingly.
8181

8282
The default strategy for message codes follows the pattern:
8383

8484
`problemDetail.[type|title|detail].[fully qualified exception class name]`
8585

86-
Some `ErrorResponse` may expose more than one message code, typically adding a suffix
86+
An `ErrorResponse` may expose more than one message code, typically adding a suffix
8787
to the default message code. The table below lists message codes, and arguments for
8888
Spring WebFlux exceptions:
8989

@@ -92,28 +92,19 @@ Spring WebFlux exceptions:
9292
|===
9393
| Exception | Message Code | Message Code Arguments
9494

95-
| `UnsupportedMediaTypeStatusException`
95+
| `HandlerMethodValidationException`
9696
| (default)
97-
| `+{0}+` the media type that is not supported, `+{1}+` list of supported media types
97+
| `+{0}+` list all validation errors.
98+
Message codes and arguments for each error are also resolved via `MessageSource`.
9899

99-
| `UnsupportedMediaTypeStatusException`
100-
| (default) + ".parseError"
101-
|
100+
| `MethodNotAllowedException`
101+
| (default)
102+
| `+{0}+` the current HTTP method, `+{1}+` the list of supported HTTP methods
102103

103104
| `MissingRequestValueException`
104105
| (default)
105106
| `+{0}+` a label for the value (e.g. "request header", "cookie value", ...), `+{1}+` the value name
106107

107-
| `UnsatisfiedRequestParameterException`
108-
| (default)
109-
| `+{0}+` the list of parameter conditions
110-
111-
| `WebExchangeBindException`
112-
| (default)
113-
| `+{0}+` the list of global errors, `+{1}+` the list of field errors.
114-
Message codes and arguments for each error within the `BindingResult` are also resolved
115-
via `MessageSource`.
116-
117108
| `NotAcceptableStatusException`
118109
| (default)
119110
| `+{0}+` list of supported media types
@@ -126,9 +117,22 @@ via `MessageSource`.
126117
| (default)
127118
| `+{0}+` the failure reason provided to the class constructor
128119

129-
| `MethodNotAllowedException`
120+
| `UnsupportedMediaTypeStatusException`
130121
| (default)
131-
| `+{0}+` the current HTTP method, `+{1}+` the list of supported HTTP methods
122+
| `+{0}+` the media type that is not supported, `+{1}+` list of supported media types
123+
124+
| `UnsupportedMediaTypeStatusException`
125+
| (default) + ".parseError"
126+
|
127+
128+
| `UnsatisfiedRequestParameterException`
129+
| (default)
130+
| `+{0}+` the list of parameter conditions
131+
132+
| `WebExchangeBindException`
133+
| (default)
134+
| `+{0}+` the list of global errors, `+{1}+` the list of field errors.
135+
Message codes and arguments for each error are also resolved via `MessageSource`.
132136

133137
|===
134138

framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/modelattrib-method-args.adoc

+8-5
Original file line numberDiff line numberDiff line change
@@ -160,10 +160,13 @@ Kotlin::
160160
----
161161
======
162162

163-
Note that use of `@ModelAttribute` is optional -- for example, to set its attributes.
164-
By default, any argument that is not a simple value type (as determined by
165-
{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty])
166-
and is not resolved by any other argument resolver is treated as if it were annotated
167-
with `@ModelAttribute`.
163+
If method validation applies because other parameters have `@Constraint` annotations,
164+
then `HandlerMethodValidationException` would be raised instead. See the section on
165+
controller method xref:web/webmvc/mvc-controller/ann-validation.adoc[Validation].
166+
167+
TIP: Using `@ModelAttribute` is optional. By default, any argument that is not a simple
168+
value type as determined by
169+
{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]
170+
_AND_ that is not resolved by any other argument resolver is treated as an `@ModelAttribute`.
168171

169172

framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/multipart-forms.adoc

+4-2
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,10 @@ Kotlin::
176176
======
177177
--
178178

179+
If method validation applies because other parameters have `@Constraint` annotations,
180+
then `HandlerMethodValidationException` is raised instead. See the section on
181+
xref:web/webflux/controller/ann-validation.adoc[Validation].
182+
179183
To access all multipart data as a `MultiValueMap`, you can use `@RequestBody`,
180184
as the following example shows:
181185

@@ -306,5 +310,3 @@ file upload.
306310

307311
Received part events can also be relayed to another service by using the `WebClient`.
308312
See xref:web/webflux-webclient/client-body.adoc#webflux-client-body-multipart[Multipart Data].
309-
310-

framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/requestbody.adoc

+29
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,33 @@ Kotlin::
8989
----
9090
======
9191

92+
You can also declare an `Errors` parameter for access to validation errors, but in
93+
that case the request body must not be a `Mono`, and will be resolved first:
94+
95+
[tabs]
96+
======
97+
Java::
98+
+
99+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
100+
----
101+
@PostMapping("/accounts")
102+
public void handle(@Valid @RequestBody Account account, Errors errors) {
103+
// use one of the onError* operators...
104+
}
105+
----
106+
107+
Kotlin::
108+
+
109+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
110+
----
111+
@PostMapping("/accounts")
112+
fun handle(@Valid @RequestBody account: Mono<Account>) {
113+
// ...
114+
}
115+
----
116+
======
117+
118+
If method validation applies because other parameters have `@Constraint` annotations,
119+
then `HandlerMethodValidationException` is raised instead. For more details, see the
120+
section on xref:web/webflux/controller/ann-validation.adoc[Validation].
92121

0 commit comments

Comments
 (0)