LoginSignup
13

More than 3 years have passed since last update.

【SpringBoot】バリデーション(@Validated / @Valid)を任意のタイミングで動かす【BeanValidation】

Last updated at Posted at 2019-07-29

検証はSpringBoot2.0.9で行いました。

TL;DR

単純なバリデーションは、javax.validation.ValidationValidation.buildDefaultValidatorFactory().getValidator()で取得したValidatorを用いることで、任意のタイミングでバリデーションできます。

@Autowiredするバリデーターを用いるアノテーションが絡むような内容は、Validator@Autowireでインジェクションして取得し用いることで、任意のタイミングでバリデーションができます。

従来の動かし方

バリデーションしたいフォームをコントローラーで受け取る場合には、以下のようにすることで、BindingResultにエラーを受け取ることができます。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

/* 省略 */

@PostMapping("/api/test-post-api")
public void editRelayStructs(
        @RequestBody @Validated Form form,
        BindingResult errorResult
) {
    /* 省略 */
}

従来手法の問題点

単体テストやパラメーターとの相関チェックなど、実際には任意のタイミングでバリデーションを行いたいという場合があります。
その場合、この上に挙げた例の形でバリデーションしてエラーを受け取ることはできません。

Validation.buildDefaultValidatorFactory().getValidator()Validatorを取得する方法

@Autowireしたいなどの理由が無ければ、javax.validation.ValidationValidation.buildDefaultValidatorFactory().getValidator()で取得したValidatorを用いることで、任意のタイミングでバリデーションができます。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;

/* 省略 */

@PostMapping("/api/test-post-api")
public void editRelayStructs(
        @RequestBody Form form
) {
    // バリデーション結果を取得
    Set<ConstraintViolation<Form>> errorResult =
            Validation.buildDefaultValidatorFactory().getValidator().validate(form);

    /* 省略 */
}

この方法は、単純なバリデーションについてはカバーできます。
一方、このやり方はSpringを介さずにバリデーションを実行するため、@Autowiredする自作アノテーションはインジェクションが機能しないという問題があります。

Validator@Autowireでインジェクションして取得する方法

前述の通り、Validation.buildDefaultValidatorFactory().getValidator()で取得したValidatorでは、@Autowiredして検証を行うようなバリデータークラスが機能しません。

@Autowiredするバリデータークラスの例
import org.springframework.beans.factory.annotation.Autowired;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class ExistingIdValidator implements ConstraintValidator<ExistingId,Integer> {
    @Autowired
    public Dao dao;

    @Override
    public boolean isValid(Integer id, ConstraintValidatorContext context) {
        if (id == null) {
            return true;
        }
        return /* Daoを用いたIDに対する存在チェック結果 */;
    }
}

この場合、Spring側に定義されているValidator@Autowireでインジェクションして取得することで、バリデータークラスでの@Autowiredが機能します。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.ConstraintViolation;
import javax.validation.Validator;

/* 省略 */

// (本来はコンストラクタパターンでインジェクションすべき)
@Autowired
public Validator validator; // Springに定義されているjavax.validation.Validator

@PostMapping("/api/test-post-api")
public void editRelayStructs(
        @RequestBody Form form
) {
    // バリデーション結果を取得
    Set<ConstraintViolation<Form>> errorResult = validator.validate(form);

    /* 省略 */
}

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13