この記事はMicroAd Advent Calendarの5日目の記事です。
TL;DR
Kotlinでバリデーションをやります。主に書くのは以下2点についてです。
- アノテーションを使ったバリデーション
- AssertTrueを使ったバリデーション
他の書き方もありますが、とりあえず自分が知っているのがこの2つなのでまとめます。
当初アノテーション自作まで書いてましたが、長くなりすぎたので分割し、7日目に回しました。
前書き
この記事は以下の記事を元に書いています。
- 【Kotlin】SpringBootでGetのコントローラーを動かすまで【SpringBoot】 - wrongwrongな開発日記
- 【Kotlin】SpringBootでControllerが受け取った内容をバリデーションするまで【SpringBoot】 - wrongwrongな開発日記
コントローラー
コントローラーは以下を用います。
受け取ったmyModelに対してバリデーションを行い、引っかかればエラーを、引っかからなければpostした内容を返しています。
import com.wrongwrong.modeltest.model.MyModel
import org.springframework.validation.BindingResult
import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.*
@RestController
@RequestMapping("my")
class MyController{
@PostMapping
fun myPostTest(
@RequestBody @Validated myModel: MyModel,
bindingResult: BindingResult
): String {
//エラーがあればエラーを文字列にして返す
if(bindingResult.hasErrors()){
return bindingResult.allErrors.toString()
}
//受け取った内容を返却
return "post:" + myModel.toString()
}
}
エラーの無い状態で叩くと以下のようになります。
$ curl -X POST -H "Content-Type: application/json" -d '{"id":1, "name":"wrong wrong", "create":"2018-11-01", "update":"2018-11-02"}' localhost:8080/my
post:MyModel(id=1, name=wrong wrong, create=Thu Nov 01 09:00:00 JST 2018, update=Fri Nov 02 09:00:00 JST 2018)
モデル
今回バリデーションを行う上で、元にするモデルは以下です。
全てNullableとしなければ正常にバリデーションが行えないので、必ずNullableで宣言してください(説明は本題と外れるので省略します)。
バリデーションしたい内容はコメントの通りです。
import java.util.*
data class MyModel(
// nullを禁止したい
val id: Long?,
// 名前がスペース区切りになっていることを確認したい
val name: String?,
// createはupdateと同じか過去にしたい
val create: Date?,
val update: Date?
)
アノテーションを使ったバリデーション
まずidのバリデーションを、アノテーションとして実装されたバリデーターを使ってやっていきます。
data classでは、@field:NotNull
というようにアノテーションを付与します。
Javaではfield:
無しでもバリデーションが行えますが、Data Classではこれを付けなければ正常にバリデーションができません。
アノテーションを付与すると以下のようになります。
import java.util.*
import javax.validation.constraints.NotNull
data class MyModel(
@field:NotNull(message = "idはnull不許可")
val id: Long?,
// 名前がスペース区切りになっていることを確認したい
val name: String?,
// createはupdateと同じか過去にしたい
val create: Date?,
val update: Date?
)
この状態でエラー有り/無しをそれぞれCurlで叩いた結果が以下です。
$ curl -X POST -H "Content-Type: application/json" -d '{"id":1}' localhost:8080/my
post:MyModel(id=1, name=null, create=null, update=null)
$ curl -X POST -H "Content-Type: application/json" -d '{}' localhost:8080/my
[Field error in object 'myModel' on field 'id': rejected value [null]; codes [NotNull.myModel.id,NotNull.id,NotNull.java.lang.Long,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [myModel.id,id]; arguments []; default message [id]]; default message [idはnull不許可]]
利用できるアノテーション
javax.validation.constraints
クラスや、org.hibernate.validator.constraints
クラスを見ておくと、既存で利用できるアノテーションは大体まとまっています。
javax.validation.constraints (Java(TM) EE 8 Specification APIs)
org.hibernate.validator.constraints (Hibernate Validator 5.1.3.Final)
AssertTrueを使ったバリデーション
次にcreateとupdateの相関チェックをやっていきます。
相関チェックはJavaと同様、@AssertTrue
(または@AssertFalse
)を付与したBool型の関数で行います。
今回は相関チェックにのみ利用していますが、アノテーションが用意されていないような単体チェックにも利用できます。
相関チェックを加えると以下のようになります。
import java.util.*
import javax.validation.constraints.AssertTrue
import javax.validation.constraints.NotNull
data class MyModel(
@field:NotNull(message = "idはnull不許可")
val id: Long?,
// 名前がスペース区切りになっていることを確認したい
val name: String?,
val create: Date?,
val update: Date?
) {
@AssertTrue(message = "updateがcreateより過去")
fun isLater(): Boolean {
if(create == null || update == null) return true
return create.before(update) || create == update
}
}
この状態でエラー有り/無しをそれぞれCurlで叩いた結果が以下です。
curl -X POST -H "Content-Type: application/json" -d '{"id":1, "create":"2018-11-03", "update":"2018-11-02"}' localhost:8080/my
[Field error in object 'myModel' on field 'later': rejected value [false]; codes [AssertTrue.myModel.later,AssertTrue.later,AssertTrue.boolean,AssertTrue]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [myModel.later,later]; arguments []; default message [later]]; default message [updateがcreateより過去]]
後書き
記事は7日目へ続きます。