Validation
Module for validating classes/records and methods using annotations.
Dependency¶
Dependency build.gradle:
Module:
Dependency build.gradle.kts:
Module:
Validation annotations¶
Special validation annotations are used by Kora to validate fields/arguments, they represent simple checks.
Available validation annotations:
- @NotEmpty- Checks that the string is not empty
- @NotBlank- Checks that the string does not consist of empty characters
- @Pattern- Checks if the string matches Regular Expression (RegEx)
- @Range- Checks that the number is in the specified range
- @Size- Checks that a collection (List, Set, Map) or- Stringhas a size in the specified range.
Class validation¶
It is suggested to use the @Valid annotation to mark a class that needs a validator from the Kora framework.
An example of a labeled class for validation looks like this:
A validator of that class will then be available in the dependency container:
Created validators can be implemented as dependencies in any component, in the examples above the validator for the Foo class,
can be implemented by its signature Validator<Foo> as a component dependency and used manually for validation.
The validator returns a list of violations after validation, they can be used to manually compose the error either
you can use the validateAndThrow method which throws a ViolationException exception in case of a validation error.
Field validation¶
It is expected to use a special provided validation annotation validation set for field validation.
An example of an object marked up for validation looks like this:
For Record classes, the syntax for accessing fields via Record-like getter contracts is used, 
in the case of Foo and the code field, getter code() will be used in the created Validator.
For a regular class it is expected that Java Getters syntax will be used, for example for the id field getter getId() will be used,
where getter should have at least package-private visibility.
Required fields¶
All fields are required (NotNull) by default, so NotNull checks will be created for all of them in the Validator.
Optional fields¶
In order to specify a field as not required, you need to mark it with any @Nullable annotation,
will not create a null check for such a field:
- Any @Nullableannotation will do, such asjavax.annotation.Nullable/jakarta.annotation.Nullable/org.jetbrains.annotations.Nullable/ etc.
It is expected to use the Kotlin Nullability syntax and mark such a field as Nullable:
Embedded fields¶
In order to validate fields of complex objects for which validators are created (or provided independently),
or fields that are not supported by standard validation tools,
the @Valid annotation is supposed to be used:
In the example above, a Validator<Bar> validator would be created for Bar and a Validator<Foo> would be created for Foo,
where when the Validator<Foo> validator is called, the validator for Validator<Bar> will be called internally.
Validation options¶
There are two types of validation:
- Full- all fields that are just marked up are checked, all possible validation errors are collected and only then an exception is thrown. (Default behavior)
- FailFast- exception is thrown on the first validation error encountered.
Example of FailFast validation:
ValidatorContext context = ValidationContext.builder().failFast(true).build();
List<Violation> violations = fooValidator.validate(value,context);
Method validation¶
It is expected to use a special provided set of annotations validation for validating method arguments and result.
Argument validation¶
It is required to use the @Validate annotation over the method to validate method arguments:
Required arguments¶
All arguments are required (NotNull) by default, so NotNull checks will be created for all of them.
Optional arguments¶
In order to specify an argument as not required requires marking it with any @Nullable annotation,
will not create a null check for such an argument:
@Component
Public class SomeService {
    @Validate
    public int validate(@Nullable String argument) { //(1)!
        return 1;
    }
}
- Any @Nullableannotation will do, such asjavax.annotation.Nullable/jakarta.annotation.Nullable/org.jetbrains.annotations.Nullable/ etc.
It is expected to use the Kotlin Nullability syntax and mark such an argument as Nullable:
Embedded arguments¶
In order to validate fields of complex objects for which validators are created (or provided independently),
or fields that are not supported by standard validation tools,
@Valid annotation is supposed to be used:
In the example above, a Validator<Bar> validator would be created for Bar and a Validator<Foo> would be created for Foo,
where when the Validator<Foo> validator is called, the validator for Validator<Bar> will be called internally.
Result validation¶
In order to validate the result of a method, it is required to use the @Validate annotation over the method and mark it up with the appropriate annotations.
In order to check that the value is not null, you need to use any @NotNull/@Nonnull annotation:
@Valid
public record Foo(@Valid Bar bar) { }
@Component
public class SomeService {
    @Size(min = 1, max = 3) //(3)!
    @Valid //(2)!
    @Validate //(1)!
    public List<Foo> validate() {
        // do something
    }
}
- Indicates that the method requires validation
- Indicates that the result requires validation with a validator from the return value type
- Standard validation annotation
@Component
open class SomeService {
    @Size(min = 1, max = 3) //(3)!
    @Valid //(2)!
    @Validate //(1)!
    fun validate(): List<Foo> {
        // do something
    }
}
- Indicates that the method requires validation
- Indicates that the result requires validation with a validator from the return value type
- Standard validation annotation
Validation options¶
There are two types of validation:
- Full- all fields that are just marked up are validated, all possible validation errors are collected and only then an exception is thrown. (Default behavior)
- FailFast- exception is thrown on the first validation error encountered.
Example of FailFast validation:
Custom validation annotations¶
Creating your custom annotation requires:
1) Create an inheritor of Validator:
final class MyValidStringValidator implements Validator<String> {
    @Nonnull
    @Override
    public List<Violation> validate(String value, @Nonnull ValidationContext context) {
        if (value == null) {
            return List.of(context.violates("Should be not empty, but was null"));
        } else if (value.isEmpty()) {
            return List.of(context.violates("Should be not empty, but was empty"));
        }
        return Collections.emptyList();
    }
}
class MyValidStringValidator : Validator<String?> {
    fun validate(value: String?, context: ValidationContext): List<Violation> {
        if (value == null) {
            return listOf(context.violates("Should be not empty, but was null"))
        } else if (value.isEmpty()) {
            return listOf(context.violates("Should be not empty, but was empty"))
        }
        return listOf()
    }
}
2) Create ValidatorFactory implementation:
3) Register the inheritor of ValidatorFactory as a component:
4) Create a validation annotation and annotate it @ValidatedBy with the previously created ValidatorFactory inheritor:
5) Annotate field/argument/result:
Signatures¶
Available signatures for repository methods out of the box:
Class must be non final in order for aspects to work.
The T refers to the type of the return value.
- T myMethod()
- Optional<T> myMethod()
- Mono<T> myMethod()Project Reactor (require dependency)
- Flux<T> myMethod()Project Reactor (require dependency)
Class must be open in order for aspects to work.
By T we mean the type of the return value, either T?, or Unit.
- myMethod(): T
- suspend myMethod(): TKotlin Coroutine (require dependency as- implementation)
- myMethod(): Flow<T>Kotlin Coroutine (require dependency as- implementation)