bean validation not working with kotlin (JSR 380)

℡╲_俬逩灬. 提交于 2020-01-02 00:13:28

问题


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

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(@Validated user: User, bindingResult: BindingResult): ModelAndView {
        return ModelAndView("someview", "user", user)
    }
}

with this being the User class written in kotlin:

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

and this being the test:

@Test
internal fun shouldNotCreateNewTestWithInvalidParams() {
    mockMvc.perform(post("/users")
        .param("roles", "invalid role"))
        .andExpect(model().attributeHasFieldErrors("user",  "roles[]"))
}

Invalid Roles are mapped to null.

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.

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

Kotlin:

private Ljava/util/Set; roles
@Ljavax/validation/constraints/NotEmpty;()
@Lorg/jetbrains/annotations/NotNull;() // added because roles is not nullable in kotlin

Now the question is why?

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


回答1:


Answer

It seems to be an issue with kotlin right now. Refer to KT-27049 for further information.


Workaround

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

The Annotation:

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>> = []
)

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

And finally the updated User class:

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

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

Java:

Kotlin:

Source is available here: https://gitlab.com/darkatra/jsr380-kotlin-issue/tree/workaround

Thanks again for your help Rafal G.




回答2:


Try adding ? like this:

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

Then the kotlin compiler should realise roles could be null, and it might honor the validation, I know little about JSR380 so i'm just guessing though.



来源:https://stackoverflow.com/questions/52345291/bean-validation-not-working-with-kotlin-jsr-380

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!