Skip to content

Commit

Permalink
docs: validation module (#8957)
Browse files Browse the repository at this point in the history
Close #8876
  • Loading branch information
sdelamo authored Mar 17, 2023
1 parent 9684680 commit 84852a7
Show file tree
Hide file tree
Showing 6 changed files with 23 additions and 156 deletions.
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ micronautsessiondocs=https://micronaut-projects.github.io/micronaut-session/snap
micronautservletdocs=https://micronaut-projects.github.io/micronaut-servlet/latest/guide/
micronautspringapi=https://micronaut-projects.github.io/micronaut-spring/latest/api
micronauttracingapi=https://micronaut-projects.github.io/micronaut-tracing/latest/api
micronautvalidationdocs=https://micronaut-projects.github.io/micronaut-validation/snapshot/guide/index.html
hibernateapi=http://docs.jboss.org/hibernate/orm/current/javadocs
rsapi=http://www.reactive-streams.org/reactive-streams-1.0.3-javadoc
projectUrl=https://micronaut.io
Expand Down
8 changes: 5 additions & 3 deletions src/main/docs/guide/aop/validation.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ Validation advice is built on https://beanvalidation.org/2.0/spec/[Bean Validati

Micronaut provides native support for the `jakarta.validation` annotations with the `micronaut-validation` dependency:

dependency::micronaut-validation[]
dependency:micronaut-validation-processor[groupId="io.micronaut.validation",scope="annotationProcessor"]

Or full JSR 380 compliance with the `micronaut-hibernate-validator` dependency:
dependency:micronaut-validation[groupId="io.micronaut.validation"]

dependency::micronaut-hibernate-validator[]
Or full https://beanvalidation.org/2.0/spec/[JSR 380] compliance with the `micronaut-hibernate-validator` dependency:

dependency::micronaut-hibernate-validator[groupId="io.micronaut.beanvalidation"]

See the section on <<beanValidation, Bean Validation>> for more information on how to apply validation rules to your bean classes.
9 changes: 9 additions & 0 deletions src/main/docs/guide/appendix/breaks.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ The instrumentation features for Reactor have been moved to the `micronaut-react

dependency:micronaut-reactor[groupId="io.micronaut.reactor"]


==== Validation Support Moved to Validation Module

The validation features link:{micronautvalidationdocs}[have been moved to a separate module]. Moreover, the new validation module requires you to use `micronaut-validation-processor` in the annotation processor classpath.

dependency:micronaut-validation-processor[groupId="io.micronaut.validation",scope="annotationProcessor"]

dependency:micronaut-validation[groupId="io.micronaut.validation"]

==== Session Support Moved to Session Module

The Session handling features link:{micronautsessiondocs}[have been moved to its own module]. If you use the HTTP session module, change the maven coordinates from `io.micronaut:micronaut-session` to `io.micronaut.session:micronaut-session`.
Expand Down
6 changes: 4 additions & 2 deletions src/main/docs/guide/httpServer/datavalidation.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ It is easy to validate incoming data with Micronaut controllers using <<validati

Micronaut provides native support for the `jakarta.validation` annotations with the `micronaut-validation` dependency:

dependency::micronaut-validation[]
dependency:micronaut-validation-processor[groupId="io.micronaut.validation",scope="annotationProcessor"]

Or full JSR 380 compliance with the `micronaut-hibernate-validator` dependency:
dependency:micronaut-validation[groupId="io.micronaut.validation"]

Or full https://beanvalidation.org/2.0/spec/[JSR 380] compliance with the `micronaut-hibernate-validator` dependency:

dependency:micronaut-hibernate-validator[groupId="io.micronaut.beanvalidation"]

Expand Down
149 changes: 1 addition & 148 deletions src/main/docs/guide/ioc/beanValidation.adoc
Original file line number Diff line number Diff line change
@@ -1,148 +1 @@
Since Micronaut 1.2, Micronaut has built-in support for validating beans annotated with `jakarta.validation` annotations. At a minimum include the `micronaut-validation` module as a compile dependency:

dependency::micronaut-validation[]

Note that Micronaut's implementation is not currently fully compliant with the https://beanvalidation.org/2.0/spec/[Bean Validator specification] as the specification heavily relies on reflection-based APIs.

The following features are unsupported at this time:

* Annotations on generic argument types, since only the Java language supports this feature.
* Any interaction with the https://beanvalidation.org/2.0/spec/#constraintmetadata[constraint metadata API], since Micronaut uses compile-time generated metadata.
* XML-based configuration
* Instead of using `jakarta.validation.ConstraintValidator`, use api:validation.validator.constraints.ConstraintValidator[] (io.micronaut.validation.validator.constraints.ConstraintValidator) to define custom constraints, which supports validating annotations at compile time.
Micronaut's implementation includes the following benefits:

* Reflection and Runtime Proxy free validation, resulting in reduced memory consumption
* Smaller JAR size since Hibernate Validator adds another 1.4MB
* Faster startup since Hibernate Validator adds 200ms+ startup overhead
* Configurability via Annotation Metadata
* Support for Reactive Bean Validation
* Support for validating the source AST at compile time
* Automatic compatibility with GraalVM native without additional configuration
If you require full Bean Validator 2.0 compliance, add the `micronaut-hibernate-validator` module to your build, which replaces Micronaut's implementation.

dependency:micronaut-hibernate-validator[groupId="io.micronaut.beanvalidation"]

=== Validating Bean Methods

You can validate methods of any class declared as a Micronaut bean by applying `jakarta.validation` annotations to arguments:

.Validating Methods
snippet::io.micronaut.docs.ioc.validation.PersonService[tags="imports,class",indent=0]

The above example declares that the `@NotBlank` annotation will be validated when invoking the `sayHello` method.

WARNING: If you use Kotlin, the class and method must be declared `open` so Micronaut can create a compile-time subclass. Alternatively you can annotate the class with ann:validation.Validated[] and configure the Kotlin `all-open` plugin to open classes annotated with this type. See the https://kotlinlang.org/docs/reference/compiler-plugins.html[Compiler plugins] section.

A `jakarta.validation.ConstraintViolationException` is thrown if a validation error occurs. For example:

.ConstraintViolationException Example
snippet::io.micronaut.docs.ioc.validation.PersonServiceSpec[tags="imports,test",indent=0]

<1> The method is called with a blank string
<2> An exception occurs

=== Validating Data Classes

To validate data classes, e.g. POJOs (typically used in JSON interchange), the class must be annotated with ann:core.annotation.Introspected[] (see the previous section on <<introspection, Bean Introspection>>) or, if the class is external, be imported by the `@Introspected` annotation.

.POJO Validation Example
snippet::io.micronaut.docs.ioc.validation.Person[tags="class"]

TIP: The ann:core.annotation.Introspected[] annotation can be used as a meta-annotation; common annotations like `@javax.persistence.Entity` are treated as `@Introspected`

The above example defines a `Person` class that has two properties (`name` and `age`) that have constraints applied. Note that in Java the annotations can be on the field or the getter, and with Kotlin data classes, the annotation should target the field.

To validate the class manually, inject an instance of api:validation.validator.Validator[]:

.Manual Validation Example
snippet::io.micronaut.docs.ioc.validation.pojo.PersonServiceSpec[tags="validator", indent="0"]

<1> The validator validates the person
<2> The constraint violations are verified

Alternatively on Bean methods you can use `jakarta.validation.Valid` to trigger cascading validation:

.ConstraintViolationException Example
snippet::io.micronaut.docs.ioc.validation.pojo.PersonService[tags="class",indent="0"]

The `PersonService` now validates the `Person` class when invoked:

.Manual Validation Example
snippet::io.micronaut.docs.ioc.validation.pojo.PersonServiceSpec[tags="validate-service",indent="0"]

<1> A validated method is invoked
<2> The constraint violations are verified

=== Validating Configuration Properties

You can also validate the properties of classes that are annotated with ann:context.annotation.ConfigurationProperties[] to ensure configuration is correct.

NOTE: It is recommended that you annotate ann:context.annotation.ConfigurationProperties[] that features validation with ann:context.annotation.Context[] to ensure that the validation occurs at startup.

=== Defining Additional Constraints

To define additional constraints, create a new annotation, for example:

.Example Constraint Annotation
snippet::io.micronaut.docs.ioc.validation.custom.DurationPattern[tags="imports,class", indent="0"]

<1> The annotation should be annotated with `jakarta.validation.Constraint`
<2> A `message` template can be provided in a hard-coded manner as above. If none is specified, Micronaut tries to find a message using `ClassName.message` using the api:context.MessageSource[] interface (optional)
<3> To support repeated annotations you can define an inner annotation (optional)

TIP: You can add messages and message bundles using the api:context.MessageSource[] and api:context.i18n.ResourceBundleMessageSource[] classes. See <<bundle, Resource Bundles>> documentation.

Once you have defined the annotation, implement a api:validation.validator.constraints.ConstraintValidator[] that validates the annotation. You can either create a bean class that implements the interface directly or define a factory that returns one or more validators.

The latter approach is recommended if you plan to define multiple validators:

.Example Constraint Validator
snippet::io.micronaut.docs.ioc.validation.custom.MyValidatorFactory[tags="imports,class", indent="0"]

<1> Override the default message template with an inline call for more control over the validation error message. (Since `2.5.0`)

The above example implements a validator that validates any field, parameter etc. that is annotated with `DurationPattern`, ensuring that the string can be parsed with `java.time.Duration.parse`.

NOTE: Generally `null` is regarded as valid and `@NotNull` is used to constrain a value as not being `null`. The example above regards `null` as a valid value.

For example:

.Example Custom Constraint Usage
snippet::io.micronaut.docs.ioc.validation.custom.HolidayService[tags="class", indent="0"]

To verify the above examples validates the `duration` parameter, define a test:

.Testing Example Custom Constraint Usage
snippet::io.micronaut.docs.ioc.validation.custom.DurationPatternValidatorSpec[tags="test", indent="0"]

<1> A validated method is invoked
<2> THe constraint violations are verified

=== Validating Annotations at Compile Time

You can use Micronaut's validator to validate annotation elements at compile time by including `micronaut-validation` in the annotation processor classpath:

dependency::micronaut-validation[scope="annotationProcessor"]

Then Micronaut will at compile time validate annotation values that are themselves annotated with `jakarta.validation`. For example consider the following annotation:

.Annotation Validation
snippet::io.micronaut.docs.ioc.validation.custom.TimeOff[tags="imports,class", indent="0"]

If you attempt to use `@TimeOff(duration="junk")` in your source, Micronaut will fail compilation due to the `duration` value violating the `DurationPattern` constraint.

NOTE: If `duration` is a property placeholder such as `@TimeOff(duration="${my.value}")`, validation is deferred until runtime.

Note that to use a custom `ConstraintValidator` at compile time you must instead define the validator as a class:

.Example Constraint Validator
snippet::io.micronaut.docs.ioc.validation.custom.DurationPatternValidator[tags="imports,class", indent="0"]

Additionally:

* Define a `META-INF/services/io.micronaut.validation.validator.constraints.ConstraintValidator` file that references the class.
* The class must be public and have a public no-argument constructor
* The class must be on the annotation processor classpath of the project to be validated.
See the link:{micronautvalidationdocs}[Micronaut Validation documentation].
6 changes: 3 additions & 3 deletions src/main/docs/guide/languageSupport/java/lombok.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ Or if using Maven:
<artifactId>micronaut-inject-java</artifactId>
<version>${micronaut.version}</version>
</path>
<path>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-validation</artifactId>
<path>
<groupId>io.micronaut.validation</groupId>
<artifactId>micronaut-validation-processor</artifactId>
<version>${micronaut.version}</version>
</path>
</annotationProcessorPaths>
Expand Down

0 comments on commit 84852a7

Please sign in to comment.