検証はSpringBoot2.0.9で行いました。
TL;DR
単純なバリデーションは、javax.validation.Validation
のValidation.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.Validation
のValidation.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
して検証を行うようなバリデータークラスが機能しません。
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);
/* 省略 */
}