bean验证不适用于kotlin(JSR 380) [英] bean validation not working with kotlin (JSR 380)

查看:142
本文介绍了bean验证不适用于kotlin(JSR 380)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,我想不出这个问题的更好的标题,因此我愿意接受更改.

so first of all i could not think of a better title for this question so i'm open for changes.

我正在尝试使用带有Spring Boot的bean验证机制(JSR-380)来验证bean.

I am trying to validate a bean using the bean validation mechanism (JSR-380) with spring boot.

所以我有一个像这样的控制器:

So i got a controller like this:

@Controller
@RequestMapping("/users")
class UserController {
    @PostMapping
    fun createUser(@Valid user: User, bindingResult: BindingResult): ModelAndView {
        return ModelAndView("someview", "user", user)
    }
}

这是用kotlin编写的User类:

with this being the User class written in 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[]"))
}

无效的角色被映射为null.

如您所见,我希望roles包含至少一个项目,且所有项目都不为空. 但是,在测试上述代码时,如果roles包含空值,则不会报告绑定错误.如果集合为空,则报告错误. 我以为这可能是kotlin代码如何编译的问题,因为当用Java编写User类时,相同的代码可以正常工作.像这样:

As you can see i want roles to contain at least one item with none of the items being null. However when testing the above code no binding errors are reported if roles contains null values. It does report an error if the set is empty though. I was thinking that this might be an issue with how kotlin code compiles as the same code works just fine when the User class is written in java. Like this:

@Data // just lombok...
public class User {
    @NotEmpty
    private Set<@NotNull Role> roles = new HashSet<>();
}

相同的控制器,相同的测试.

Same Controller, same test.

检查字节码后,我注意到kotlin版本不包括嵌套的@NotNull批注(请参见下文).

After checking the bytecode i noticed that the kotlin version is not including the nested @NotNull annotation (see below).

Java:

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. this does not affect validation

现在的问题是为什么?

这是一个示例项目,以防您想尝试一些东西

Here's a sample project in case you want to try some stuff.

推荐答案

答案

这似乎是kotlin的问题.有关更多信息,请参考 KT-27049 .

RafalG.已经指出,我们可以使用自定义验证器作为解决方法.所以这是一些代码:

Rafal G. already pointed out that we could use a custom validator as a workaround. So here's some code:

注释:

import javax.validation.Constraint
import javax.validation.Payload
import kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS
import kotlin.annotation.AnnotationTarget.CONSTRUCTOR
import kotlin.annotation.AnnotationTarget.FIELD
import kotlin.annotation.AnnotationTarget.FUNCTION
import kotlin.annotation.AnnotationTarget.TYPE_PARAMETER
import kotlin.annotation.AnnotationTarget.VALUE_PARAMETER
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略有不同.例如,elementTypepropertyPath有所不同,如下所示.


Altough validation works now, the resulting ConstrainViolation is slightly different. For example the elementType and propertyPath differs as you can see below.

Java:

科特琳:

可在此处找到源: https://gitlab.com/darkatra/jsr380-kotlin-issue/tree/workaround

再次感谢您的帮助 Rafal G.

这篇关于bean验证不适用于kotlin(JSR 380)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆