diff --git a/framework-docs/modules/ROOT/nav.adoc b/framework-docs/modules/ROOT/nav.adoc
index a98c10f8d2b..24123d403f6 100644
--- a/framework-docs/modules/ROOT/nav.adoc
+++ b/framework-docs/modules/ROOT/nav.adoc
@@ -267,6 +267,7 @@
***** xref:web/webmvc/mvc-controller/ann-methods/jackson.adoc[]
**** xref:web/webmvc/mvc-controller/ann-modelattrib-methods.adoc[]
**** xref:web/webmvc/mvc-controller/ann-initbinder.adoc[]
+**** xref:web/webmvc/mvc-controller/ann-validation.adoc[]
**** xref:web/webmvc/mvc-controller/ann-exceptionhandler.adoc[]
**** xref:web/webmvc/mvc-controller/ann-advice.adoc[]
*** xref:web/webmvc-functional.adoc[]
@@ -361,6 +362,7 @@
***** xref:web/webflux/controller/ann-methods/jackson.adoc[]
**** xref:web/webflux/controller/ann-modelattrib-methods.adoc[]
**** xref:web/webflux/controller/ann-initbinder.adoc[]
+**** xref:web/webflux/controller/ann-validation.adoc[]
**** xref:web/webflux/controller/ann-exceptions.adoc[]
**** xref:web/webflux/controller/ann-advice.adoc[]
*** xref:web/webflux-functional.adoc[]
diff --git a/framework-docs/modules/ROOT/pages/core/validation/beanvalidation.adoc b/framework-docs/modules/ROOT/pages/core/validation/beanvalidation.adoc
index 3837d50a355..b8fb7381282 100644
--- a/framework-docs/modules/ROOT/pages/core/validation/beanvalidation.adoc
+++ b/framework-docs/modules/ROOT/pages/core/validation/beanvalidation.adoc
@@ -123,15 +123,12 @@ Validator, is expected to be present in the classpath and is automatically detec
[[validation-beanvalidation-spring-inject]]
-=== Injecting a Validator
+=== Inject Jakarta Validator
`LocalValidatorFactoryBean` implements both `jakarta.validation.ValidatorFactory` and
-`jakarta.validation.Validator`, as well as Spring's `org.springframework.validation.Validator`.
-You can inject a reference to either of these interfaces into beans that need to invoke
-validation logic.
-
-You can inject a reference to `jakarta.validation.Validator` if you prefer to work with the Bean
-Validation API directly, as the following example shows:
+`jakarta.validation.Validator`, so you can inject a reference to the latter to
+apply validation logic if you prefer to work with the Bean Validation API directly,
+as the following example shows:
[tabs]
======
@@ -160,8 +157,15 @@ Kotlin::
----
======
-You can inject a reference to `org.springframework.validation.Validator` if your bean
-requires the Spring Validation API, as the following example shows:
+
+[[validation-beanvalidation-spring-inject-adapter]]
+=== Inject Spring Validator
+
+In addition to implementing `jakarta.validation.Validator`, `LocalValidatorFactoryBean`
+also adapts to `org.springframework.validation.Validator`, so you can inject a reference
+to the latter if your bean requires the Spring Validation API.
+
+For example:
[tabs]
======
@@ -190,9 +194,15 @@ Kotlin::
----
======
+When used as `org.springframework.validation.Validator`, `LocalValidatorFactoryBean`
+invokes the underlying `jakarta.validation.Validator`, and then adapts
+``ContraintViolation``s to ``FieldError``s, and registers them with the `Errors` object
+passed into the `validate` method.
+
+
[[validation-beanvalidation-spring-constraints]]
-=== Configuring Custom Constraints
+=== Configure Custom Constraints
Each bean validation constraint consists of two parts:
@@ -274,9 +284,8 @@ As the preceding example shows, a `ConstraintValidator` implementation can have
[[validation-beanvalidation-spring-method]]
=== Spring-driven Method Validation
-You can integrate the method validation feature supported by Bean Validation 1.1 (and, as
-a custom extension, also by Hibernate Validator 4.3) into a Spring context through a
-`MethodValidationPostProcessor` bean definition:
+You can integrate the method validation feature of Bean Validation into a
+Spring context through a `MethodValidationPostProcessor` bean definition:
[tabs]
======
@@ -305,11 +314,11 @@ XML::
----
======
-To be eligible for Spring-driven method validation, all target classes need to be annotated
+To be eligible for Spring-driven method validation, target classes need to be annotated
with Spring's `@Validated` annotation, which can optionally also declare the validation
groups to use. See
{api-spring-framework}/validation/beanvalidation/MethodValidationPostProcessor.html[`MethodValidationPostProcessor`]
-for setup details with the Hibernate Validator and Bean Validation 1.1 providers.
+for setup details with the Hibernate Validator and Bean Validation providers.
[TIP]
====
@@ -320,6 +329,61 @@ xref:core/aop/proxying.adoc#aop-understanding-aop-proxies[Understanding AOP Prox
to always use methods and accessors on proxied classes; direct field access will not work.
====
+By default, `jakarta.validation.ConstraintViolationException` is raised with the set of
+``ConstraintViolation``s returned by `jakarata.validation.Validator`. As an alternative,
+you can have `MethodValidationException` raised instead with ``ConstraintViolation``s
+adapted to `MessageSourceResolvable` errors. To enable set the following flag:
+
+[tabs]
+======
+Java::
++
+[source,java,indent=0,subs="verbatim,quotes",role="primary"]
+----
+ import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
+
+ @Configuration
+ public class AppConfig {
+
+ @Bean
+ public MethodValidationPostProcessor validationPostProcessor() {
+ MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
+ processor.setAdaptConstraintViolations(true);
+ return processor;
+ }
+ }
+
+----
+
+XML::
++
+[source,xml,indent=0,subs="verbatim,quotes",role="secondary"]
+----
+
+
+
+----
+======
+
+`MethodValidationException` contains a list of ``ParameterValidationResult``s which
+group errors by method parameter, and each exposes a `MethodParameter`, the argument
+value, and a list of `MessageSourceResolvable` errors adapted from
+``ConstraintViolation``s. For `@Valid` method parameters with cascaded violations on
+fields and properties, the `ParameterValidationResult` is `ParameterErrors` which
+implements `org.springframework.validation.Errors` and exposes validation errors as
+``FieldError``s.
+
+The adapted `MessageSourceResolvable` errors can be turned into error messages to
+display to users through the configured
+xref:core/beans/context-introduction.adoc#context-functionality-messagesource[`MessageSource`]
+based on locale and language specific resource bundles.
+
+NOTE: Spring MVC and WebFlux have built-in support for method validation, and therefore
+for web controller methods there is no need for a class level `@Validated` and an AOP proxy.
+See the Spring MVC xref:web/webmvc/mvc-controller/ann-validation.adoc[Validation] section,
+the WebFlux xref:web/webflux/controller/ann-validation.adoc[Validation] section,
+and the xref:web/webmvc/mvc-controller/ann-validation.adoc[Error Responses] section.
+
diff --git a/framework-docs/modules/ROOT/pages/web/webflux/ann-rest-exceptions.adoc b/framework-docs/modules/ROOT/pages/web/webflux/ann-rest-exceptions.adoc
index 5be40fd19a0..ec887afa1c0 100644
--- a/framework-docs/modules/ROOT/pages/web/webflux/ann-rest-exceptions.adoc
+++ b/framework-docs/modules/ROOT/pages/web/webflux/ann-rest-exceptions.adoc
@@ -67,23 +67,23 @@ from an existing `ProblemDetail`. This could be done centrally, e.g. from an
[[webflux-ann-rest-exceptions-i18n]]
-== Internationalization
+== Customization and i18n
[.small]#xref:web/webmvc/mvc-ann-rest-exceptions.adoc#mvc-ann-rest-exceptions-i18n[See equivalent in the Servlet stack]#
-It is a common requirement to internationalize error response details, and good practice
-to customize the problem details for Spring WebFlux exceptions. This section describes the
-support for that.
+It is a common requirement to customize and internationalize error response details.
+It is also good practice to customize the problem details for Spring WebFlux exceptions
+to avoid revealing implementation details. This section describes the support for that.
-`ErrorResponse` exposes message codes for "type", "title", and "detail", in addition to
+An `ErrorResponse` exposes message codes for "type", "title", and "detail", as well as
message code arguments for the "detail" field. `ResponseEntityExceptionHandler` resolves
these through a xref:core/beans/context-introduction.adoc#context-functionality-messagesource[MessageSource]
-and updates the `ProblemDetail` accordingly.
+and updates the corresponding `ProblemDetail` fields accordingly.
The default strategy for message codes follows the pattern:
`problemDetail.[type|title|detail].[fully qualified exception class name]`
-Some `ErrorResponse` may expose more than one message code, typically adding a suffix
+An `ErrorResponse` may expose more than one message code, typically adding a suffix
to the default message code. The table below lists message codes, and arguments for
Spring WebFlux exceptions:
@@ -92,28 +92,19 @@ Spring WebFlux exceptions:
|===
| Exception | Message Code | Message Code Arguments
-| `UnsupportedMediaTypeStatusException`
+| `HandlerMethodValidationException`
| (default)
-| `+{0}+` the media type that is not supported, `+{1}+` list of supported media types
+| `+{0}+` list all validation errors.
+Message codes and arguments for each error are also resolved via `MessageSource`.
-| `UnsupportedMediaTypeStatusException`
-| (default) + ".parseError"
-|
+| `MethodNotAllowedException`
+| (default)
+| `+{0}+` the current HTTP method, `+{1}+` the list of supported HTTP methods
| `MissingRequestValueException`
| (default)
| `+{0}+` a label for the value (e.g. "request header", "cookie value", ...), `+{1}+` the value name
-| `UnsatisfiedRequestParameterException`
-| (default)
-| `+{0}+` the list of parameter conditions
-
-| `WebExchangeBindException`
-| (default)
-| `+{0}+` the list of global errors, `+{1}+` the list of field errors.
-Message codes and arguments for each error within the `BindingResult` are also resolved
-via `MessageSource`.
-
| `NotAcceptableStatusException`
| (default)
| `+{0}+` list of supported media types
@@ -126,9 +117,22 @@ via `MessageSource`.
| (default)
| `+{0}+` the failure reason provided to the class constructor
-| `MethodNotAllowedException`
+| `UnsupportedMediaTypeStatusException`
| (default)
-| `+{0}+` the current HTTP method, `+{1}+` the list of supported HTTP methods
+| `+{0}+` the media type that is not supported, `+{1}+` list of supported media types
+
+| `UnsupportedMediaTypeStatusException`
+| (default) + ".parseError"
+|
+
+| `UnsatisfiedRequestParameterException`
+| (default)
+| `+{0}+` the list of parameter conditions
+
+| `WebExchangeBindException`
+| (default)
+| `+{0}+` the list of global errors, `+{1}+` the list of field errors.
+Message codes and arguments for each error are also resolved via `MessageSource`.
|===
diff --git a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/modelattrib-method-args.adoc b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/modelattrib-method-args.adoc
index 347a025545a..809104fe1e6 100644
--- a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/modelattrib-method-args.adoc
+++ b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/modelattrib-method-args.adoc
@@ -160,10 +160,13 @@ Kotlin::
----
======
-Note that use of `@ModelAttribute` is optional -- for example, to set its attributes.
-By default, any argument that is not a simple value type (as determined by
-{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty])
-and is not resolved by any other argument resolver is treated as if it were annotated
-with `@ModelAttribute`.
+If method validation applies because other parameters have `@Constraint` annotations,
+then `HandlerMethodValidationException` would be raised instead. See the section on
+controller method xref:web/webmvc/mvc-controller/ann-validation.adoc[Validation].
+
+TIP: Using `@ModelAttribute` is optional. By default, any argument that is not a simple
+value type as determined by
+{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]
+_AND_ that is not resolved by any other argument resolver is treated as an `@ModelAttribute`.
diff --git a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/multipart-forms.adoc b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/multipart-forms.adoc
index 3ded73de6d6..462bf3eccb1 100644
--- a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/multipart-forms.adoc
+++ b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/multipart-forms.adoc
@@ -176,6 +176,10 @@ Kotlin::
======
--
+If method validation applies because other parameters have `@Constraint` annotations,
+then `HandlerMethodValidationException` is raised instead. See the section on
+xref:web/webflux/controller/ann-validation.adoc[Validation].
+
To access all multipart data as a `MultiValueMap`, you can use `@RequestBody`,
as the following example shows:
@@ -306,5 +310,3 @@ file upload.
Received part events can also be relayed to another service by using the `WebClient`.
See xref:web/webflux-webclient/client-body.adoc#webflux-client-body-multipart[Multipart Data].
-
-
diff --git a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/requestbody.adoc b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/requestbody.adoc
index 8190c281125..b4b78afd28d 100644
--- a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/requestbody.adoc
+++ b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/requestbody.adoc
@@ -89,4 +89,33 @@ Kotlin::
----
======
+You can also declare an `Errors` parameter for access to validation errors, but in
+that case the request body must not be a `Mono`, and will be resolved first:
+
+[tabs]
+======
+Java::
++
+[source,java,indent=0,subs="verbatim,quotes",role="primary"]
+----
+ @PostMapping("/accounts")
+ public void handle(@Valid @RequestBody Account account, Errors errors) {
+ // use one of the onError* operators...
+ }
+----
+
+Kotlin::
++
+[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
+----
+ @PostMapping("/accounts")
+ fun handle(@Valid @RequestBody account: Mono) {
+ // ...
+ }
+----
+======
+
+If method validation applies because other parameters have `@Constraint` annotations,
+then `HandlerMethodValidationException` is raised instead. For more details, see the
+section on xref:web/webflux/controller/ann-validation.adoc[Validation].
diff --git a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-validation.adoc b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-validation.adoc
new file mode 100644
index 00000000000..8b0c069db65
--- /dev/null
+++ b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-validation.adoc
@@ -0,0 +1,111 @@
+[[mvc-ann-validation]]
+= Validation
+
+[.small]#xref:web/webmvc/mvc-controller/ann-validation.adoc[See equivalent in the Servlet stack]#
+
+Spring WebFlux has built-in xref:core/validation/validator.adoc[Validation] support for
+`@RequestMapping` methods, including the option to use
+xref:core/validation/beanvalidation.adoc[Java Bean Validation].
+The validation support works on two levels.
+
+First, method parameters such as
+xref:web/webflux/controller/ann-methods/modelattrib-method-args.adoc[@ModelAttribute],
+xref:web/webflux/controller/ann-methods/requestbody.adoc[@RequestBody], and
+xref:web/webflux/controller/ann-methods/multipart-forms.adoc[@RequestPart] do perform
+validation if annotated with Jakarta's `@Valid` or Spring's `@Validated` annotation, and
+raise `MethodArgumentNotValidException` in case of validation errors. If you want to handle
+the errors in the controller method instead, you can declare an `Errors` or `BindingResult`
+method parameter immediately after the validated parameter.
+
+Second, if https://beanvalidation.org/[Java Bean Validation] is present _AND_ other method
+parameters, e.g. `@RequestHeader`, `@RequestParam`, `@PathVariable` have `@Constraint`
+annotations, then method validation is applied to all method arguments, raising
+`HandlerMethodValidationException` in case of validation errors. You can still declare an
+`Errors` or `BindingResult` after an `@Valid` method parameter, and handle validation
+errors within the controller method, as long as there are no validation errors on other
+method arguments.
+
+You can configure a `Validator` globally through the
+xref:web/webflux/config.adoc#webflux-config-validation[WebMvc config], or locally
+through an xref:web/webflux/controller/ann-initbinder.adoc[@InitBinder] method in an
+`@Controller` or `@ControllerAdvice`. You can also use multiple validators.
+
+NOTE: If a controller has a class level `@Validated`, then
+xref:core/validation/beanvalidation.adoc#validation-beanvalidation-spring-method[method validation is applied]
+through an AOP proxy. In order to take advantage of the Spring MVC built-in support for
+method validation added in Spring Framework 6.1, you need to remove the class level
+`@Validated` annotation from the controller.
+
+The xref:web/webmvc/mvc-ann-rest-exceptions.adoc[Error Responses] section provides further
+details on how `MethodArgumentNotValidException` and `HandlerMethodValidationException`
+are handled, and also how their rendering can be customized through a `MessageSource` and
+locale and language specific resource bundles.
+
+For further custom handling of method validation errors, you can extend
+`ResponseEntityExceptionHandler` or use an `@ExceptionHandler` method in a controller
+or in a `@ControllerAdvice`, and handle `HandlerMethodValidationException` directly.
+The exception contains a list of``ParameterValidationResult``s that group validation errors
+by method parameter. You can either iterate over those, or provide a visitor with callback
+methods by controller method parameter type:
+
+[tabs]
+======
+Java::
++
+[source,java,indent=0,subs="verbatim,quotes",role="primary"]
+----
+ HandlerMethodValidationException ex = ... ;
+
+ ex.visitResults(new HandlerMethodValidationException.Visitor() {
+
+ @Override
+ public void requestHeader(RequestHeader requestHeader, ParameterValidationResult result) {
+ // ...
+ }
+
+ @Override
+ public void requestParam(@Nullable RequestParam requestParam, ParameterValidationResult result) {
+ // ...
+ }
+
+ @Override
+ public void modelAttribute(@Nullable ModelAttribute modelAttribute, ParameterErrors errors) {
+
+ // ...
+
+ @Override
+ public void other(ParameterValidationResult result) {
+ // ...
+ }
+ });
+----
+
+Kotlin::
++
+[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
+----
+ // HandlerMethodValidationException
+ val ex
+
+ ex.visitResults(object : HandlerMethodValidationException.Visitor {
+
+ override fun requestHeader(requestHeader: RequestHeader, result: ParameterValidationResult) {
+ // ...
+ }
+
+ override fun requestParam(requestParam: RequestParam?, result: ParameterValidationResult) {
+ // ...
+ }
+
+ override fun modelAttribute(modelAttribute: ModelAttribute?, errors: ParameterErrors) {
+ // ...
+ }
+
+ // ...
+
+ override fun other(result: ParameterValidationResult) {
+ // ...
+ }
+ })
+----
+======
diff --git a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-ann-rest-exceptions.adoc b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-ann-rest-exceptions.adoc
index 79329becc28..1f4d9ff1fd9 100644
--- a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-ann-rest-exceptions.adoc
+++ b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-ann-rest-exceptions.adoc
@@ -67,23 +67,23 @@ from an existing `ProblemDetail`. This could be done centrally, e.g. from an
[[mvc-ann-rest-exceptions-i18n]]
-== Internationalization
+== Customization and i18n
[.small]#xref:web/webflux/ann-rest-exceptions.adoc#webflux-ann-rest-exceptions-i18n[See equivalent in the Reactive stack]#
-It is a common requirement to internationalize error response details, and good practice
-to customize the problem details for Spring WebFlux exceptions. This section describes the
-support for that.
+It is a common requirement to customize and internationalize error response details.
+It is also good practice to customize the problem details for Spring MVC exceptions
+to avoid revealing implementation details. This section describes the support for that.
-`ErrorResponse` exposes message codes for "type", "title", and "detail", in addition to
+An `ErrorResponse` exposes message codes for "type", "title", and "detail", as well as
message code arguments for the "detail" field. `ResponseEntityExceptionHandler` resolves
these through a xref:core/beans/context-introduction.adoc#context-functionality-messagesource[MessageSource]
-and updates the `ProblemDetail` accordingly.
+and updates the corresponding `ProblemDetail` fields accordingly.
The default strategy for message codes follows the pattern:
`problemDetail.[type|title|detail].[fully qualified exception class name]`
-Some `ErrorResponse` may expose more than one message code, typically adding a suffix
+An `ErrorResponse` may expose more than one message code, typically adding a suffix
to the default message code. The table below lists message codes, and arguments for
Spring MVC exceptions:
@@ -100,6 +100,11 @@ Spring MVC exceptions:
| (default)
| `+{0}+` property name, `+{1}+` property value
+| `HandlerMethodValidationException`
+| (default)
+| `+{0}+` list all validation errors.
+Message codes and arguments for each error are also resolved via `MessageSource`.
+
| `HttpMediaTypeNotAcceptableException`
| (default)
| `+{0}+` list of supported media types
@@ -131,8 +136,7 @@ Spring MVC exceptions:
| `MethodArgumentNotValidException`
| (default)
| `+{0}+` the list of global errors, `+{1}+` the list of field errors.
- Message codes and arguments for each error within the `BindingResult` are also resolved
- via `MessageSource`.
+ Message codes and arguments for each error are also resolvedvia `MessageSource`.
| `MissingRequestHeaderException`
| (default)
diff --git a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-config/validation.adoc b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-config/validation.adoc
index 833914f4ab3..b307b8fc8c8 100644
--- a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-config/validation.adoc
+++ b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-config/validation.adoc
@@ -6,7 +6,7 @@
By default, if xref:core/validation/beanvalidation.adoc#validation-beanvalidation-overview[Bean Validation] is present
on the classpath (for example, Hibernate Validator), the `LocalValidatorFactoryBean` is
registered as a global xref:core/validation/validator.adoc[Validator] for use with `@Valid` and
-`Validated` on controller method arguments.
+`@Validated` on controller method arguments.
In Java configuration, you can customize the global `Validator` instance, as the
following example shows:
diff --git a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc
index 1136ab94958..1add700c9e2 100644
--- a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc
+++ b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc
@@ -144,7 +144,7 @@ Java::
}
@PostMapping("update")
- public String update(@Valid AccountForm form, BindingResult result,
+ public String update(AccountForm form, BindingResult result,
@ModelAttribute(binding=false) Account account) { // <1>
// ...
}
@@ -166,7 +166,7 @@ Kotlin::
}
@PostMapping("update")
- fun update(@Valid form: AccountForm, result: BindingResult,
+ fun update(form: AccountForm, result: BindingResult,
@ModelAttribute(binding = false) account: Account): String { // <1>
// ...
}
@@ -210,10 +210,15 @@ Kotlin::
<1> Validate the `Pet` instance.
======
-Note that using `@ModelAttribute` is optional (for example, to set its attributes).
-By default, any argument that is not a simple value type (as determined by
-{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty])
-and is not resolved by any other argument resolver is treated as if it were annotated
-with `@ModelAttribute`.
+If an `@ModelAttribute` is declared without `BindingResult` parameter after it, then
+`MethodArgumentNotValueException` is raised. However, if method validation applies because
+other parameters have `@Constraint` annotations, then `HandlerMethodValidationException`
+is raised instead. For more details, see the section on
+xref:web/webmvc/mvc-controller/ann-validation.adoc[Validation].
+
+TIP: Using `@ModelAttribute` is optional. By default, any parameter that is not a simple
+value type as determined by
+{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]
+_AND_ that is not resolved by any other argument resolver is treated as an `@ModelAttribute`.
diff --git a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/multipart-forms.adoc b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/multipart-forms.adoc
index de91ecad48d..5e4addcb3a2 100644
--- a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/multipart-forms.adoc
+++ b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/multipart-forms.adoc
@@ -188,8 +188,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@PostMapping("/")
- public String handle(@Valid @RequestPart("meta-data") MetaData metadata,
- BindingResult result) {
+ public String handle(@Valid @RequestPart("meta-data") MetaData metadata, Errors errors) {
// ...
}
----
@@ -199,12 +198,14 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@PostMapping("/")
- fun handle(@Valid @RequestPart("meta-data") metadata: MetaData,
- result: BindingResult): String {
+ fun handle(@Valid @RequestPart("meta-data") metadata: MetaData, errors: Errors): String {
// ...
}
----
======
+If method validation applies because other parameters have `@Constraint` annotations,
+then `HandlerMethodValidationException` is raised instead. For more details, see the
+section on xref:web/webmvc/mvc-controller/ann-validation.adoc[Validation].
diff --git a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/requestbody.adoc b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/requestbody.adoc
index 2c4f316feb9..9cdc1e8fb07 100644
--- a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/requestbody.adoc
+++ b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/requestbody.adoc
@@ -48,7 +48,7 @@ Java::
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
@PostMapping("/accounts")
- public void handle(@Valid @RequestBody Account account, BindingResult result) {
+ public void handle(@Valid @RequestBody Account account, Errors errors) {
// ...
}
----
@@ -58,10 +58,13 @@ Kotlin::
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
@PostMapping("/accounts")
- fun handle(@Valid @RequestBody account: Account, result: BindingResult) {
+ fun handle(@Valid @RequestBody account: Account, errors: Errors) {
// ...
}
----
======
+If method validation applies because other parameters have `@Constraint` annotations,
+then `HandlerMethodValidationException` is raised instead. For more details, see the
+section on xref:web/webmvc/mvc-controller/ann-validation.adoc[Validation].
diff --git a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-validation.adoc b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-validation.adoc
new file mode 100644
index 00000000000..a1528b55343
--- /dev/null
+++ b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-validation.adoc
@@ -0,0 +1,112 @@
+[[mvc-ann-validation]]
+= Validation
+
+[.small]#xref:web/webflux/controller/ann-validation.adoc[See equivalent in the Reactive stack]#
+
+Spring MVC has built-in xref:core/validation/validator.adoc[Validation] support for
+`@RequestMapping` methods, including the option to use
+xref:core/validation/beanvalidation.adoc[Java Bean Validation].
+The validation support works on two levels.
+
+First, method parameters such as
+xref:web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc[@ModelAttribute],
+xref:web/webmvc/mvc-controller/ann-methods/requestbody.adoc[@RequestBody], and
+xref:web/webmvc/mvc-controller/ann-methods/multipart-forms.adoc[@RequestPart] do perform
+validation if annotated with Jakarta's `@Valid` or Spring's `@Validated` annotation, and
+raise `MethodArgumentNotValidException` in case of validation errors. If you want to handle
+the errors in the controller method instead, you can declare an `Errors` or `BindingResult`
+method parameter immediately after the validated parameter.
+
+Second, if https://beanvalidation.org/[Java Bean Validation] is present _AND_ other method
+parameters, e.g. `@RequestHeader`, `@RequestParam`, `@PathVariable` have `@Constraint`
+annotations, then method validation is applied to all method arguments, raising
+`HandlerMethodValidationException` in case of validation errors. You can still declare an
+`Errors` or `BindingResult` after an `@Valid` method parameter, and handle validation
+errors within the controller method, as long as there are no validation errors on other
+method arguments. Method validation is also applied to the return value if the method
+is annotated with `@Valid` or has other `@Constraint` annotations.
+
+You can configure a `Validator` globally through the
+xref:web/webmvc/mvc-config/validation.adoc[WebMvc config], or locally through an
+xref:web/webmvc/mvc-controller/ann-initbinder.adoc[@InitBinder] method in an
+`@Controller` or `@ControllerAdvice`. You can also use multiple validators.
+
+NOTE: If a controller has a class level `@Validated`, then
+xref:core/validation/beanvalidation.adoc#validation-beanvalidation-spring-method[method validation is applied]
+through an AOP proxy. In order to take advantage of the Spring MVC built-in support for
+method validation added in Spring Framework 6.1, you need to remove the class level
+`@Validated` annotation from the controller.
+
+The xref:web/webmvc/mvc-ann-rest-exceptions.adoc[Error Responses] section provides further
+details on how `MethodArgumentNotValidException` and `HandlerMethodValidationException`
+are handled, and also how their rendering can be customized through a `MessageSource` and
+locale and language specific resource bundles.
+
+For further custom handling of method validation errors, you can extend
+`ResponseEntityExceptionHandler` or use an `@ExceptionHandler` method in a controller
+or in a `@ControllerAdvice`, and handle `HandlerMethodValidationException` directly.
+The exception contains a list of``ParameterValidationResult``s that group validation errors
+by method parameter. You can either iterate over those, or provide a visitor with callback
+methods by controller method parameter type:
+
+[tabs]
+======
+Java::
++
+[source,java,indent=0,subs="verbatim,quotes",role="primary"]
+----
+ HandlerMethodValidationException ex = ... ;
+
+ ex.visitResults(new HandlerMethodValidationException.Visitor() {
+
+ @Override
+ public void requestHeader(RequestHeader requestHeader, ParameterValidationResult result) {
+ // ...
+ }
+
+ @Override
+ public void requestParam(@Nullable RequestParam requestParam, ParameterValidationResult result) {
+ // ...
+ }
+
+ @Override
+ public void modelAttribute(@Nullable ModelAttribute modelAttribute, ParameterErrors errors) {
+
+ // ...
+
+ @Override
+ public void other(ParameterValidationResult result) {
+ // ...
+ }
+ });
+----
+
+Kotlin::
++
+[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
+----
+ // HandlerMethodValidationException
+ val ex
+
+ ex.visitResults(object : HandlerMethodValidationException.Visitor {
+
+ override fun requestHeader(requestHeader: RequestHeader, result: ParameterValidationResult) {
+ // ...
+ }
+
+ override fun requestParam(requestParam: RequestParam?, result: ParameterValidationResult) {
+ // ...
+ }
+
+ override fun modelAttribute(modelAttribute: ModelAttribute?, errors: ParameterErrors) {
+ // ...
+ }
+
+ // ...
+
+ override fun other(result: ParameterValidationResult) {
+ // ...
+ }
+ })
+----
+======
\ No newline at end of file