Валидация
Модуль для валидации моделей и методов с помощью аннотаций аспектов.
Подключение¶
Зависимость build.gradle
:
Модуль:
Зависимость build.gradle.kts
:
Модуль:
Аннотации валидации¶
Специальные аннотации валидации используются Kora для проверки значений полей классов или аргументов метода.
Доступные аннотации валидации:
@NotEmpty
- Проверяет что строка не пустая@NotBlank
- Проверяет что строка не состоит из пустых символов@Pattern
- Проверяет соответствие Regular Expression (RegEx)@Range
- Проверяет что число находится в заданном диапазоне@Size
- Проверяет что коллекция (List, Set, Map) илиString
имеет размер в заданном диапазоне
Валидация класса¶
Предлагается использовать аннотацию @Valid
для маркировки класса которому требуется создать валидатор посредствам Kora.
Затем в контейнере зависимостей будет доступен валидатор такого класса:
Созданнные валидаторы могут быть внедрены как зависимости в любой компонент, на примерах выше валидатор для класса Foo
,
может быть внедрен по своей сигнатуре Validator<Foo>
как зависимость компонента и использовать вручную для валидации.
Валидатор после валидации возвращает список нарушений, они могут использоваться для ручного составление ошибки либо
можно использовать метод validateAndThrow
который бросит исключение ViolationException
в случае ошибки валидации.
Валидация полей¶
Предполагается использовать для валидации полей специальный предоставляемый набор аннотаций валидации.
Пример размеченного для валидации объекта выглядит так:
Для Record классов используется синтаксис доступа к полям через Record-like контракты геттеров,
в случае Foo
и поля code
будет использоваться getter code()
в созданом Validator
.
Для обычного класса ожидается что будет использоваться синтаксис Java Getters, например для поля id
будет использоваться getter getId()
,
где getter должен иметь минимум package-private видимость.
Обязательные поля¶
Предполагается что все поля по умолчанию являются обязательными (NotNull
), значит для всех них будут созданы NotNull
проверки в Validator
.
Необязательные поля¶
Чтобы указать поле как не обязательное, требуется пометить его любой @Nullable
аннотацией,
для такого поля не будет создана проверка на null:
- Подойдет любая аннотация
@Nullable
, такие какjavax.annotation.Nullable
/jakarta.annotation.Nullable
/org.jetbrains.annotations.Nullable
/ и т.д.
Предполагается использовать Kotlin Nullability синтаксис и помечать такое поле как Nullable:
Вложенные поля¶
Для валидации полей сложных объектов для которых созданы валидаторы (или предоставлены самостоятельно),
либо полей которые не поддерживаются стандартными средствами валидации,
предполагается использовать @Valid
аннотацию:
В примере выше для Bar
будет создан валидатор Validator<Bar>
и для Foo
будет создан Validator<Foo>
,
где при вызове валидатора Validator<Foo>
будет вызываться внутри валидатор для Validator<Bar>
.
Опции валидации¶
Есть два вида валидации:
Full
- проверяются все поля которые только размечены, собираются все возможные ошибки валидации и только потом бросается исключение. (Поведение по умолчанию)FailFast
- исключение бросается на первой встреченной ошибке валидации.
Пример FailFast валидации:
ValidatorContext context = ValidationContext.builder().failFast(true).build();
List<Violation> violations = fooValidator.validate(value,context);
Валидация метода¶
Предполагается использовать для валидации аргументов метода и результата специальный предоставляемый набор аннотаций валидации.
Валидация аргументов¶
Чтобы провалидировать аргументы методы, требуется использовать аннотацию @Validate
над методом:
Обязательные аргументы¶
Предполагается что все аргументы по умолчанию являются обязательными (NotNull
), значит для всех них будут созданы NotNull
проверки.
Необязательные аргументы¶
Чтобы указать аргумент как не обязательное, требуется пометить его любой @Nullable
аннотацией,
для такого аргумента не будет создана проверка на null:
@Component
public class SomeService {
@Validate
public int validate(@Nullable String argument) { //(1)!
return 1;
}
}
- Подойдет любая аннотация
@Nullable
, такие какjavax.annotation.Nullable
/jakarta.annotation.Nullable
/org.jetbrains.annotations.Nullable
/ и т.д.
Предполагается использовать Kotlin Nullability синтаксис и помечать такой аргумент как Nullable:
Вложенные аргументы¶
Для валидации полей сложных объектов для которых созданы валидаторы (или предоставлены самостоятельно),
либо полей которые не поддерживаются стандартными средствами валидации,
предполагается использовать @Valid
аннотацию:
В примере выше для Bar
будет создан валидатор Validator<Bar>
и для Foo
будет создан Validator<Foo>
,
где при вызове валидатора Validator<Foo>
будет вызываться внутри валидатор для Validator<Bar>
.
Валидация результата¶
Чтобы провалидировать результат метода, требуется использовать аннотацию @Validate
над методом и разметить его соответствующими аннотациями:
@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
}
}
- Указывает что метод требует валидации
- Указывает что результат требуется валидировать валидатором с типа возвращаемого значения
- Стандартная аннотация валидации
@Component
open class SomeService {
@Size(min = 1, max = 3) //(3)!
@Valid //(2)!
@Validate //(1)!
fun validate(): List<Foo> {
// do something
}
}
- Указывает что метод требует валидации
- Указывает что результат требуется валидировать валидатором с типа возвращаемого значения
- Стандартная аннотация валидации
Опции валидации¶
Есть два вида валидации:
Full
- проверяются все поля которые только размечены, собираются все возможные ошибки валидации и только потом бросается исключение. (Поведение по умолчанию)FailFast
- исключение бросается на первой встреченной ошибке валидации.
Пример FailFast валидации:
Собственные аннотации валидации¶
Для создания собственной аннотации требуется:
1) Создать наследника 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) Создать наследника ValidatorFactory
:
3) Зарегистрировать наследника ValidatorFactory
как компонент:
4) Создать аннотацию валидации и проаннотировать ее @ValidatedBy
с ранее созданным наследником ValidatorFactory
:
5) Проаннотировать поле/аргумент/результат:
Сигнатуры¶
Доступные сигнатуры для методов которые поддерживают аннотации из коробки:
Класс не должен быть final
, чтобы аспекты работали.
Под T
подразумевается тип возвращаемого значения.
T myMethod()
Optional<T> myMethod()
CompletionStage<T> myMethod()
CompletionStageMono<T> myMethod()
Project Reactor (надо подключить зависимость)Flux<T> myMethod()
Project Reactor (надо подключить зависимость)
Класс должен быть open
, чтобы аспекты работали.
Под T
подразумевается тип возвращаемого значения, либо T?
, либо Unit
.
myMethod(): T
suspend myMethod(): T
Kotlin Coroutine (надо подключить зависимость какimplementation
)myMethod(): Flow<T>
Kotlin Coroutine (надо подключить зависимость какimplementation
)