Document method validation improvements

Closes gh-30643
This commit is contained in:
rstoyanchev
2023-07-05 17:07:30 +01:00
parent a03a14797f
commit df2617d575
13 changed files with 409 additions and 69 deletions

View File

@@ -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`.

View File

@@ -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].

View File

@@ -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].

View File

@@ -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) {
// ...
}
})
----
======