はじめに
Springにて入力パラメータとしてFormに数値型(IntegerやLong)で定義した場合、全角数字で入力した場合も正常にバインドされます。
しかし、これだと、半角数字だけを許容したいという場面に対応出来ません。
入力チェックのためだけにフィールドをString型で定義して半角数字のみを許容するようにPatternアノテーションを付与して入力規則を定義するのも何だかな、と思っていました。
そこで、なんかいい方法がないか探していたところ、WebDataBinderを利用しCustomNumberEditorを拡張したクラスを登録すればよいことがわかりました。
やり方
まず、CustomNumberEditorを拡張したクラスを作成します。
public class SampleCustomNumberEditor extends CustomNumberEditor {
private final Class<? extends Number> numberClass;
public SampleCustomNumberEditor (Class<? extends Number> numberClass, boolean allowEmpty) {
super(numberClass, allowEmpty);
this.numberClass = numberClass;
}
@Override
public void setAsText(String s) throws IllegalArgumentException {
// 値が入力されている場合のみ処理する
if (StringUtils.hasText(s)) {
// 半角の整数の場合
if (s.matches("^(\\+|\\-)?[0-9]*$")) {
// スーパークラスのメソッドに処理を移譲
super.setAsText(s);
return;
}
// 小数点付きのクラスの場合
if (numberClass == Float.class || numberClass == Double.class
|| numberClass == BigDecimal.class) {
// 半角の小数点つき数値文字列以外の場合(この例では".9"のような表現も許容)
if (!s.matches("^((\\+|\\-)?[0-9]+)?\\.[0-9]+$")) {
throw new IllegalArgumentException("半角数値以外が入力されています");
}
} else {
throw new IllegalArgumentException("半角の整数以外が入力されています");
}
}
// 半角数値以外を弾いた後はスーパークラスのメソッドを呼び出し処理を移譲
super.setAsText(s);
}
}
Controllerクラス、もしくは、全処理で適応したい場合はControllerAdviceアノテーションを付与したクラスに、@InitBinderアノテーションを付与した以下のメソッドを作成し、WebDataBinderのregisterCustomEditorメソッドで、前述で作成したクラスを登録します。
@ControllerAdvice
public class SampleControllerAdvice {
@InitBinder
public void initBinder(WebDataBinder binder) {
// Integer型を登録する
binder.registerCustomEditor(Integer.class, new SampleCustomNumberEditor(Integer.class, true));
// Long型を登録する
binder.registerCustomEditor(Long.class, new SampleCustomNumberEditor(Long.class, true));
// ...
// Double型を登録する
binder.registerCustomEditor(Double.class, new SampleCustomNumberEditor(Double.class, true));
// BigDecimal型を登録する
binder.registerCustomEditor(BigDecimal.class, new SampleCustomNumberEditor(BigDecimal.class, true));
}
}
上記で登録したすべての数値型に関しては、Formに半角数値以外が入力された場合は、入力エラーとして処理されます。必要に応じて使う型だけ登録すればよいかと思います。
なお、デフォルトでは、CustomNumberEditorが有効になります。
その他
とりあえずInteger型だけ対応したい、というケースにおいては、わざわざEditorクラスを作成しなくても、以下のように匿名クラスで対応できます。
@ControllerAdvice
public class SampleControllerAdvice {
@InitBinder
public void initBinder(WebDataBinder binder) {
// Integer型を登録する
binder.registerCustomEditor(Integer.class, new CustomNumberEditor(Integer.class, true) {
@Override
public void setAsText(String s) throws IllegalArgumentException {
// 値が入力されている場合のみ処理する
if (StringUtils.hasText(s)) {
// 半角の整数以外の場合
if (!s.matches("^(\\+|\\-)?[0-9]*$")) {
throw new IllegalArgumentException("半角数値以外が入力されています");
}
}
super.setAsText(s);
}
});
}
}