проверка бобов не работает с котлин (JSR 380)
так что в первую очередь я не мог придумать лучшего названия для этого вопроса, поэтому я открыт для изменений.
Я пытаюсь проверить компонент, используя механизм проверки компонентов (JSR-380) с загрузкой весны.
Итак, я получил контроллер вроде этого:
@Controller
@RequestMapping("/users")
class UserController {
@PostMapping
fun createUser(@Validated user: User, bindingResult: BindingResult): ModelAndView {
return ModelAndView("someview", "user", user)
}
}
с этим будет класс User, написанный в kotlin:
data class User(
@field:NotEmpty
var roles: MutableSet<@NotNull Role> = HashSet()
)
и это испытание:
@Test
internal fun shouldNotCreateNewTestWithInvalidParams() {
mockMvc.perform(post("/users")
.param("roles", "invalid role"))
.andExpect(model().attributeHasFieldErrors("user", "roles[]"))
}
Недействительные роли отображаются в нуль.
Как вы можете видеть, я хочу, чтобы roles
содержали хотя бы один элемент, при этом ни один из элементов не был бы нулевым. Однако при тестировании вышеуказанного кода ошибки привязки не сообщаются, если roles
содержат нулевые значения. Он сообщает об ошибке, если набор пуст, хотя. Я думал, что это может быть проблемой с тем, как компилируется код kotlin, поскольку тот же самый код работает очень хорошо, когда класс User написан в java. Как это:
@Data // just lombok...
public class User {
@NotEmpty
private Set<@NotNull Role> roles = new HashSet<>();
}
Тот же контроллер, тот же тест.
После проверки байт-кода я заметил, что версия kotlin не включает вложенную аннотацию @NotNull
(см. Ниже).
Джава:
private Ljava/util/Set; roles
@Ljavax/validation/constraints/NotEmpty;()
@Ljavax/validation/constraints/NotNull;() : FIELD, 0;
@Ljavax/validation/constraints/NotEmpty;() : FIELD, null
Котлин:
private Ljava/util/Set; roles
@Ljavax/validation/constraints/NotEmpty;()
@Lorg/jetbrains/annotations/NotNull;() // added because roles is not nullable in kotlin
Теперь вопрос в том, почему?
Вот пример проекта, если вы хотите попробовать что-то.
Ответы
Ответ 1
Ответ
Кажется, сейчас проблема с котлином. Обратитесь к KT-27049 за дополнительной информацией.
Временное решение
Рафал Г. уже указал, что мы можем использовать специальный валидатор в качестве обходного пути. Итак, вот какой код:
Аннотация:
import javax.validation.Constraint
import javax.validation.Payload
import kotlin.annotation.AnnotationTarget.*
import kotlin.reflect.KClass
@MustBeDocumented
@Constraint(validatedBy = [NoNullElementsValidator::class])
@Target(allowedTargets = [FUNCTION, FIELD, ANNOTATION_CLASS, CONSTRUCTOR, VALUE_PARAMETER, TYPE_PARAMETER])
@Retention(AnnotationRetention.RUNTIME)
annotation class NoNullElements(
val message: String = "must not contain null elements",
val groups: Array<KClass<out Any>> = [],
val payload: Array<KClass<out Payload>> = []
)
ConstraintValidator:
import javax.validation.ConstraintValidator
import javax.validation.ConstraintValidatorContext
class NoNullElementsValidator : ConstraintValidator<NoNullElements, Collection<Any>> {
override fun isValid(value: Collection<Any>?, context: ConstraintValidatorContext): Boolean {
// null values are valid
if (value == null) {
return true
}
return value.stream().noneMatch { it == null }
}
}
И, наконец, обновленный класс User:
data class User(
@field:NotEmpty
@field:NoNullElements
var roles: MutableSet<Role> = HashSet()
)
В настоящее время уже выполняется валидация, получившаяся ConstrainViolation немного отличается. Например, elementType
и propertyPath
отличаются, как вы можете видеть ниже.
Джава:
Котлин:
Источник доступен здесь: https://gitlab.com/darkatra/jsr380-kotlin-issue/tree/workaround
Еще раз спасибо за вашу помощь Rafal G.
Ответ 2
Попробуйте добавить ?
как это:
data class User(
@field:Valid
@field:NotEmpty
var roles: MutableSet<@NotNull Role?> = HashSet()
)
Тогда компилятор kotlin должен понимать, что роли могут быть null
, и это может означать проверку, я мало знаю о JSR380, поэтому я просто догадываюсь.