Skip to content

Commit

Permalink
Document method validation improvements
Browse files Browse the repository at this point in the history
Closes gh-30643
  • Loading branch information
rstoyanchev committed Jul 5, 2023
1 parent a03a147 commit df2617d
Show file tree
Hide file tree
Showing 13 changed files with 409 additions and 69 deletions.
2 changes: 2 additions & 0 deletions framework-docs/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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[]
Expand Down Expand Up @@ -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[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]
======
Expand Down Expand Up @@ -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]
======
Expand Down Expand Up @@ -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:

Expand Down Expand Up @@ -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]
======
Expand Down Expand Up @@ -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]
====
Expand All @@ -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"]
----
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
<property name="adaptConstraintViolations" value="true"/>
</bean>
----
======

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




Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:

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

|===

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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`.


Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -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].


Original file line number Diff line number Diff line change
Expand Up @@ -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<Account>) {
// ...
}
----
======

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

0 comments on commit df2617d

Please sign in to comment.