Help us understand the problem. What is going on with this article?

【Kotlin】バリデーションの書き方の基礎【SpringBoot】

More than 1 year has passed since last update.

この記事はMicroAd Advent Calendarの5日目の記事です。

TL;DR

Kotlinでバリデーションをやります。主に書くのは以下2点についてです。

  1. アノテーションを使ったバリデーション
  2. AssertTrueを使ったバリデーション

他の書き方もありますが、とりあえず自分が知っているのがこの2つなのでまとめます。
当初アノテーション自作まで書いてましたが、長くなりすぎたので分割し、7日目に回しました。

前書き

この記事は以下の記事を元に書いています。

コントローラー

コントローラーは以下を用います。
受け取ったmyModelに対してバリデーションを行い、引っかかればエラーを、引っかからなければpostした内容を返しています。

MyController.kt
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で宣言してください(説明は本題と外れるので省略します)。
バリデーションしたい内容はコメントの通りです。

MyModel.kt
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ではこれを付けなければ正常にバリデーションができません。
アノテーションを付与すると以下のようになります。

MyModel.kt
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型の関数で行います。
今回は相関チェックにのみ利用していますが、アノテーションが用意されていないような単体チェックにも利用できます。
相関チェックを加えると以下のようになります。

MyModel.kt
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日目へ続きます。

参考にさせていただいた記事

wrongwrong
public class Main { public static void main(String[] args) { console.log("Hello World!"); } } Error: java: シンボルを見つけられません シンボル: 変数 console 場所: クラス com.wrongwrong.Main
https://wrongwrong163377.hatenablog.com/
microad
データとテクノロジーをかけ合わせたマーケティングプラットフォームを提供する会社です。
https://www.microad.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした