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

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

検証は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);

    /* 省略 */
}
wrongwrong
リフレクションを用いたKotlin JVM向けのマッピングライブラリを作ってる人です。 https://github.com/ProjectMapK
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