home wiki.fukuchiharuki.me
Menu

キーワード

  • Spring Boot
  • Validation/Validator
  • Kotlin

したいこと

Kotlinでカスタム・バリデータを作成したい。

Javaでやるのと同じようにすればいいんだけど、Kotlinの文法的にちょっと躓いたのでメモ。

どうやって

相関チェック:時間入力の前後チェックをしたいとして。

Period.kt

package me.fukuchiharuki.example.validation.constraints

import me.fukuchiharuki.example.validation.PeriodValidator
import javax.validation.Constraint
import javax.validation.Payload
import kotlin.reflect.KClass

@Target(AnnotationTarget.CLASS, AnnotationTarget.FIELD, AnnotationTarget.ANNOTATION_CLASS)
@Retention(AnnotationRetention.RUNTIME)
@Constraint(validatedBy = [PeriodValidator::class])
annotation class Period(

        val message: String = "{me.fukuchiharuki.example.validation.constraints.Period}",
        val groups: Array<KClass<*>> = [],
        val payload: Array<KClass<out Payload>> = [],

        val startField: String = "start",
        val endField: String = "end"

)

PeriodValidator.kt

package me.fukuchiharuki.example.validation

import me.fukuchiharuki.example.validation.constraints.Period
import org.springframework.beans.BeanWrapperImpl
import java.time.LocalTime
import javax.validation.ConstraintValidator
import javax.validation.ConstraintValidatorContext

class PeriodValidator: ConstraintValidator<Period, Any> {

    private var message: String = ""
    private var startField: String = ""
    private var endField: String = ""

    override fun initialize(constraintAnnotation: Period?) {
        if (constraintAnnotation === null) return
        message = constraintAnnotation.message
        startField = constraintAnnotation.startField
        endField = constraintAnnotation.endField
    }

    override fun isValid(value: Any?, context: ConstraintValidatorContext?): Boolean {
        val beanWrapper = BeanWrapperImpl(value)
        val startTimeInput = beanWrapper.getPropertyValue(startField).toString()
        val endTimeInput = beanWrapper.getPropertyValue(endField).toString()
        if (チェックする(startTimeInput, endTimeInput)) return true
        setContext(context!!)
        return false
    }

    private fun setContext(context: ConstraintValidatorContext) {
        context.disableDefaultConstraintViolation()
        context.buildConstraintViolationWithTemplate(message)
                .addPropertyNode(endField)
                .addConstraintViolation()
    }

}

ちなみに

constraints.Periodはネストした子側のクラスのアノテーションにすることも、ネストした親側のプロパティのアノテーションにすることもできる。

  • 子側のクラスのアノテーションにする場合
    • 親側のプロパティのアノテーションには次を書いて
      @field: Valid
    • 子側のクラスのアノテーションに次を書く
      @constraints.Period
  • 親側のプロパティのアノテーションにする場合
    • 親側のプロパティのアノテーションに次を書く
      @constraints.Period

親側のプロパティのアノテーションに書く場合、「@field:」が要らないのね、なんで?

  • Object でなくて Any を使う

参考