はじめに
ぶっちゃけBean Validationの拡張方法です
確認環境
- 開発環境
- JDK 1.8
- Spring Framework 4.3
- BeanValidation 1.1
- 動作環境
- JBoss EAP 7.1
※フレームワークとしてTERASOLUNA Server Framework for Java (5.3.0.RELEASE)を使用しています。
そもそもバリデータを作るには?
まずはアノテーションを作る
ほとんどConstraint compositionにあるコードのコピペですが
AllowSingleInput.java
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
@Documented
@Constraint(validatedBy = {AllowSingleInputValidator.class}) // ↓で作るクラス
@Target({TYPE, ANNOTATION_TYPE}) // クラスまたはアノテーションにのみ付加できるようにする
@Retention(RUNTIME)
public @interface AllowSingleInput {
String message() default "{com.neriudon.example.validator.SingleInput.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String[] fields(); // 配列にする
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Documented
@interface List {
AllowSingleInput[] value();
}
}
ポイントはアノテーションのターゲットをクラスまたはアノテーションのみにすることと、String
の配列をフィールドに持たせることです。
メッセージはべた書きしてもいいのですが、プロパティでPlease input to only one text filed
を設定しています。
バリデータを作る
さて、実際の入力チェックをするクラスを作ります。
まぁ基本はExampleのコピペなのですが
AllowSingleInputValidator.java
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.util.StringUtils;
public class AllowSingleInputValidator implements ConstraintValidator<AllowSingleInput, Object> {
private String[] fields;
private String message;
@Override
public void initialize(AllowSingleInput constraintAnnotation) {
fields = constraintAnnotation.fields(); // フィールド名を取得
message = constraintAnnotation.message();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
BeanWrapper beanWrapper = new BeanWrapperImpl(value);
// いずれか1つのフィールドに入力されたかチェックする処理
boolean isSingleInput = false;
for (String field : fields) {
if (!StringUtils.isEmpty(beanWrapper.getPropertyValue(field))) {
if (isSingleInput) {
isSingleInput = false;
break;
} else {
isSingleInput = true;
}
}
}
if (isSingleInput) {
return true;
} else {
context.disableDefaultConstraintViolation();
// 条件を満たさない場合はメッセージ出力
context.buildConstraintViolationWithTemplate(message).addPropertyNode(fields[0])
.addConstraintViolation();
return false;
}
}
}
initialize
で対象のフィールド名を取得し、isValid
でいずれか1つのフィールドだけに入力されているかをチェックしています(もしかしたら他にいい書き方があるかも)。
使い方
Form
クラスにアノテーションを付与し、fields
に入力チェックをするフィールド名を設定します。
@AllowSingleInput(fields = {"todoTitle1", "todoTitle2", "todoTitle3"})
public class TodoForm implements Serializable {
private static final long serialVersionUID = 1L;
private String todoTitle1;
private String todoTitle2;
private String todoTitle3;
// getter/setterは省略します
}
jspは特に変更する必要はありません。
<!-- Formのところだけ抜き出します -->
<form:form action="${pageContext.request.contextPath}/todo/create"
method="post" modelAttribute="todoForm">
<form:label path="todoTitle1"/>
<form:input path="todoTitle1" />
<form:errors path="todoTitle1" cssClass="text-error" /><br>
<form:label path="todoTitle2"/>
<form:input path="todoTitle2" />
<form:errors path="todoTitle2" cssClass="text-error" /><br>
<form:label path="todoTitle3"/>
<form:input path="todoTitle3" />
<form:errors path="todoTitle3" cssClass="text-error" /><br>
<form:button>Create Todo</form:button>
</form:form>
実行結果
複数のフィールドに入力してボタンを押すとエラーメッセージが表示されるようになりました。
この投稿に至った経緯
携わっている案件で、このバリデータが必要になったためメモとして。