概要
Spring MVCにてフォームの入力チェックを行う方法についてまとめた。
今回は相関チェックルールを作成する方法について紹介する。
前提
入力チェックに必要な情報は以下となる。
概要 Springのメッセージ管理を行うMessageSourceを使用して外部ファイルからメッセージを取得する方法についてまとめた。 MessageSourceはプロパティファイルに定義したメッセージを取得する機能を提供する。 […]
概要 Spring MVCにてフォームの入力チェックを行うための準備と入力チェックの全体像についてまとめた。 入力チェックにはBean Validationを使用する。 Bean Validationの機能を使用するためには、その実装[…]
概要 Spring MVCにてフォームの入力チェックを行う方法についてまとめた。 入力チェックには、フォームのフィールドに入力チェック用のアノテーションを付与する。 今回は基礎的なBean Validationと、その実装であるH[…]
概要 Spring MVCにてフォームの入力チェックを行う方法についてまとめた。 今回は独自の単項目入力チェックルールを作成する方法について紹介する。 前提 入力チェックに必要な情報は以下となる。 [sit[…]
相関チェックルールの追加
2つの入力項目にて同じ内容が入力されていることを確認するための独自アノテーションを作成する。
相関チェックを作成するために必要なクラスは以下となる。
②上記の具体的なバリデーションロジックを提供するクラス
独自アノテーションクラス
相関チェックを行うための独自アノテーションクラスの例は以下となる。
FieldsMatch.java
package com.example.prototype.web.common.validation;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
/**
* 相関チェック用のアノテーションクラス.<br>
*
*/
@Documented
@Constraint(validatedBy = { FieldsMatchValidator.class }) // ①
@Retention(RUNTIME)
@Target({ TYPE, ANNOTATION_TYPE })
public @interface FieldsMatch {
String message() default "{FieldsMatch.default.message}"; // ②
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/** チェック対象 */
String property(); // ③
/** 比較対象 */
String comparingProperty(); // ③
}
@Constraintアノテーションに、当アノテーションの具体的なバリデーションロジッククラス(後ほど紹介)を指定する。
上記はメッセージプロパティのエラーメッセージキーを指定している。
何もしなければエラー時は上記のメッセージキーに紐づくメッセージ内容が表示される。
String property(); // ③
/** 比較対象 */
String comparingProperty(); // ③
相関チェックを行うための2つのフィールドを定義する。
バリデーションロジッククラス
具体的な入力チェックを行うクラスを作成する。
FieldsMatchValidator.java
package com.example.prototype.web.common.validation;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.util.ObjectUtils;
/**
* 相関チェック用のバリデーションロジッククラス.<br>
*
*/
public class FieldsMatchValidator implements ConstraintValidator<FieldsMatch, Object> { // ①
/** チェック対象 */
private String property;
/** 比較対象 */
private String comparingProperty;
@Override
public void initialize(FieldsMatch constraintAnnotation) {
// フィールド名の初期化 ②
this.property = constraintAnnotation.property();
this.comparingProperty = constraintAnnotation.comparingProperty();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
// フィールドの値を取得 // ③
var beanWrapper = new BeanWrapperImpl(value);
Object propertyValue = beanWrapper.getPropertyValue(property);
Object complaringPropertyValue = beanWrapper.getPropertyValue(comparingProperty);
// プロパティの比較 // ④
boolean res = ObjectUtils.nullSafeEquals(propertyValue, complaringPropertyValue);
// チェック結果判定 ⑤
if (!res) {
// デフォルトのメッセージを使用する場合
context.buildConstraintViolationWithTemplate(
context.getDefaultConstraintMessageTemplate()).addPropertyNode(property)
.addConstraintViolation();
}
return res;
}
}
ConstraintValidatorインターフェースを実装し、以下のメソッドをオーバーライドする。
・isValid: 入力値検証を行う
this.property = constraintAnnotation.property();
this.comparingProperty = constraintAnnotation.comparingProperty();
相関チェックを行う2つのフィールド名をセットする。
var beanWrapper = new BeanWrapperImpl(value);
Object propertyValue = beanWrapper.getPropertyValue(property);
Object complaringPropertyValue = beanWrapper.getPropertyValue(comparingProperty);
Springの提供するBeanWrapperImplを使用して、value(フォームオブジェクト)をラップしたオブジェクトを作成する。
BeanWrapperImplにフィールド名を渡すことで、フォームに格納された値を取得できる。
上記ではpropertyとcomparingPropertyに指定したフィールドの値を取得している。
boolean res = ObjectUtils.nullSafeEquals(propertyValue, complaringPropertyValue);
ObjectUtils.nullSafeEqualsメソッドを使用して、2つのフィールドの値を比較している。
上記メソッドはnull値でも安全に比較できる。
2つのフィールドが等しい場合はtrueを、等しくない場合はfalseを返却する。
if (!res) {
上記のブロックでは入力チェックエラー時のエラーメッセージの挙動について実装している。
デフォルトのエラーメッセージ(messageフィールド)を使用するか、
デフォルト以外のカスタムメッセージを使用するかで実装方法が異なる。
デフォルトのエラーメッセージを使用
デフォルトのエラーメッセージを使用する場合、以下のように実装する。
FieldsMatchValidator.java
// デフォルトのメッセージを使用する場合
context.buildConstraintViolationWithTemplate(
context.getDefaultConstraintMessageTemplate()).addPropertyNode(property)
.addConstraintViolation();
上記はエラーメッセージ表示に必要。
カスタムエラーメッセージを使用
デフォルトエラーメッセージではなく、独自のエラーメッセージを使用したい場合は以下のように実装する。
FieldsMatchValidator.java
// カスタムメッセージを使用する場合
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("{FieldsMatch.custom.message}")
.addPropertyNode(property).addConstraintViolation();
上記はデフォルトのエラーメッセージを表示しないようにする設定。
※実装しないとデフォルトのエラーとカスタムのエラーどちらも表示されてしまう
上記はデフォルトで定義しているエラーメッセージではなく、”{FieldsMatch.custom.message}”というエラーメッセージキーを定義している。
使用方法
作成した独自アノテーションを使用する。
今回は「パスワード」と「パスワード(確認用)」フィールドを用いて、どちらも同じ値であることを確認する。
FooForm.java
@Data
@FieldsMatch(property = "password", comparingProperty = "confirmPassword") // ①
public class FooForm implements Serializable {
// フォーム部品を追加していく
// パスワード
@Alphanumeric
private String password;
// 確認用パスワード
private String confirmPassword;
}
作成した相関チェックアノテーションはクラスに付与する。
propertyとcomparingProperty属性に相関チェックを適用したいフィールドを指定する。
メッセージプロパティには以下のように定義する。
messages_ja.properties
fooForm.password=パスワード
fooForm.confirmPassword=パスワード(確認用)
FieldsMatch.default.message={0}と{1}の値が一致しません。
エラーの結果は以下となった。
メッセージプロパティに定義したプレースフォルダに、正しくバインドされない事象が起きた。
2つめのプロパティ名は「フォーム名.フィールド名」という記載で定義すると、正しく適用されない模様。
以下のようにメッセージ定義を修正した。
messages_ja.properties
fooForm.password=パスワード
confirmPassword=パスワード(確認用)
FieldsMatch.default.message={0}と{1}の値が一致しません。
実行結果