まず第一に、私はこの質問のためのより良いタイトルを考えることができなかったので、私は変更を受け入れるつもりです。
スプリングブートでBean検証メカニズム(JSR-380)を使用してBeanを検証しようとしています。
だから私はこのようなコントローラーを得ました:
@Controller
@RequestMapping("/users")
class UserController {
@PostMapping
fun createUser(@Valid 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
には少なくとも1つのアイテムを含め、どのアイテムもnullにしないようにします。ただし、上記のコードをテストすると、roles
にnull値が含まれている場合、バインディングエラーは報告されません。ただし、セットが空の場合はエラーを報告します。これは、UserクラスがJavaで記述されている場合、同じコードが正常に機能するため、kotlinコードのコンパイル方法に問題があるのではないかと考えていました。このような:
@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. this does not affect validation
今問題はなぜですか?
これは、何かを試したい場合のために サンプルプロジェクト です
現在、kotlinの問題のようです。詳細は KT-27049 を参照してください。
Rafal G。 カスタムバリデーターを回避策として使用できることをすでに指摘しました。だからここにいくつかのコードがあります:
注釈:
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 }
}
}
そして最後に更新されたユーザークラス:
data class User(
@field:NotEmpty
@field:NoNullElements
var roles: MutableSet<Role> = HashSet()
)
検証は現在機能していますが、結果のConstrainViolationは少し異なります。たとえば、以下のようにelementType
とpropertyPath
は異なります。
Java:
Kotlin:
ソースはここにあります: https://gitlab.com/darkatra/jsr380-kotlin-issue/tree/workaround
ご協力ありがとうございます Rafal G。
次のように?
を追加してみてください:
data class User(
@field:Valid
@field:NotEmpty
var roles: MutableSet<@NotNull Role?> = HashSet()
)
次に、kotlinコンパイラはロールがnull
である可能性があることを認識し、検証を尊重する可能性があります。JSR380についてはほとんど知らないので、私は推測しています。