SpringBootで@Componentの関数の引数にバリデーションをかける

SpringBootで、@Component内の関数の引数にバリデーションする方法を探したのでメモ

リクエストの内容をバリデーションするためにControllerクラスへの引数に @Validを付与してバリデーションを行う方法はよく見かけるが、実際のアプリケーションだとユースケース層の処理やドメインの処理への引数(=入出力としてではなくビジネスロジックとしての値)をバリデーションしたいことが多いはず(というのも、コントローラーに書くと入出力の数が増えるごとにバリデーションされる箇所が増えていくため)

前提として、spring-boot-starter-validationが依存関係としてインストールされており以下のようなバリデーションの対象クラスと実行する関数を持つクラスが存在するとする。

// バリデーションの対象になるクラス
data class EmployeeForm(
        @field:NotBlank  // kotlinの場合、`field:`を付与しないとビルド時にフィールドのアノテーションと解釈されない
        val name: String,
        @field:Past      // kotlinの場合、`field:`を付与しないとビルド時にフィールドのアノテーションと解釈されない
        val dateOfBirth: LocalDate
)

// バリデーションを実行する関数を持つクラス
@Component
class HogehogeUsecase() {
    fun registerEmployee(
      employeeForm: EmployeeForm
    ) {
        // ...
    }
}

この場合HogehogeUsecaseにアノテーションを2つ加えるだけ。ただし該当のクラスがbeanとして登録されている必要がある

// バリデーションを実行する関数を持つクラス
@Component
+@Validated  // 1. org.springframework.validation.annotation.Validated を付与
class HogehogeUsecase() {
    fun registerEmployee(
+      @Valid employeeForm: EmployeeForm // 2. javax.validation.Valid を付与
-      employeeForm: EmployeeForm
    ) {
        // ...
    }
}

これだけで引数に与えられた値がバリデートされ、もしエラーがなければ関数内の処理が実行される。もしエラーが1つでも存在すれば、 javax.validation.ConstraintViolationExceptionがthrowされる。

例えばエラーメッセージをhtml上に出すために整形したければ以下のように呼び出し元でcatchすることもできる

try {
  hogehogeUsecase.registerEmployee(employeeForm)
} catch (e: ConstraintViolationException) {
  val message = e.constraintViolations.joinToString("\n") {
      it.message
  }
}

もし引数がList<EmployeeForm>というように複雑だった場合でもこの方法で対応可能。

参考にしたサイトなど↓


See also