bean验证不适用于kotlin(JSR 380)

时间:2018-09-15 13:47:11

标签: java spring-boot kotlin bean-validation jsr380

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

我正在尝试通过带有Spring Boot的bean验证机制(JSR-380)来验证bean。

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

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

这是用kotlin编写的User类:

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类时,相同的代码可以正常工作。像这样:

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

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

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

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

现在的问题是为什么?

这里是sample project,以防您想尝试一些东西。

2 个答案:

答案 0 :(得分:2)

尝试像这样添加?

data class User(
    @field:Valid
    @field:NotEmpty
    var roles: MutableSet<@NotNull Role?> = HashSet()
)

然后kotlin编译器应该意识到角色可能是null,并且它可能会兑现验证,我对JSR380知之甚少,所以我只是在猜测。

答案 1 :(得分:2)

答案

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


解决方法

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

注释:

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 }
    }
}

ConstraintValidator:

data class User(
    @field:NotEmpty
    @field:NoNullElements
    var roles: MutableSet<Role> = HashSet()
)

最后是更新的User类:

elementType

现在可以进行所有验证,结果ConstrainViolation略有不同。例如,propertyPath和{{1}}如下所示。

Java:

The Java Version

科特琳:

The Kotlin Version

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

再次感谢您的帮助Rafal G.