バリデーション

Cubby ではフォームから入力された値をバリデーションする仕組みが用意されています。

フォームから入力された値は HttpServletRequest#getParameterValues で返される値が String の配列であることからもわかるように、 全ての値を配列として取得できます。定義済みのバリデーションはこの配列に対してのバリデーションを行います。

また、フォームオブジェクトやデータベースを参照してデータ制約を検証する独自のバリデーションをプログラム上に定義することができます。

バリデーションの実行タイミングはリクエストからアクション実行までのフローを参照してください。

バリデーションの定義

バリデーションの定義は Java コードで記述します。以下のように、アクションメソッドに対してバリデーションのルールを定義します。

  1. アクションにValidationRules型のプロパティを用意し、 定義したバリデーションのルールを取得できるようにします。

    以下のようにDefaultValidationRulesを継承して、 initializeメソッドをオーバーライドし、そのメソッドの中で addメソッドを用いて フォームのフィールドごとにバリデーションのルールを追加します。

    public class LoginAction extends Action {
    
      // バリデーションのルール
      public ValidationRules validationRules = new DefaultValidationRules() {
        @Override
        public void initialize() {
          // フィールド "userId" は必須入力で最大10文字まで
          add("userId", new RequiredValidator(), new MaxLengthValidator(10));
    
          // フィールド "password" は必須入力で最大10文字まで
          add("password", new RequiredValidator(), new MaxLengthValidator(10));
        }
      };
      ...
  2. アクションメソッドにアノテーション@Validationを定義し、rulesでアクションメソッドと[[1]]で定義したバリデーションルールのプロパティを関連付けます。

    入力検証でエラーを検出した場合は、アクションのプロパティerrorsにメッセージが設定され、errorPageで指定されたエラーページへフォワードで遷移します。

      // プロパティ「validationRules」をバリデーションのルールとして使用します。
      // 検証エラー発生時は「confirm.jsp」に遷移します。
      @Validation(rules = "validationRules", errorPage = "confirm.jsp")
      public ActionResult save1() {
        ... 
      }
    
    }

定義済みバリデーション

Cubby に定義済みのバリデーションクラスは以下のものがあります。

ScalarFieldValidator を実装しているクラスがひとつの要素を検証するバリデータ、 ArrayFieldValidator を実装しているクラスが配列全体を検証するバリデータとなっています。

クラス名 説明
ArrayMaxSizeValidator 配列の最大サイズを検証します。
ArrayMinSizeValidator 配列の最小サイズを検証します。
DateFormatValidator 日付に対する検証を行います。
EmailValidator Eメールアドレスに対する検証を行います。
EqualsValidator 指定した文字列と等しいかどうかを検証します。
FileRegexpValidator ファイルアップロードのファイル名が指定された正規表現にマッチするか検証します。
MaxLengthValidator 最大文字数を検証します。
NumberValidator 数値かどうかを検証します。
RangeLengthValidator 文字列の長さの範囲を指定して検証します。
RangeValidator 数値の範囲を指定して検証します。
RegexpValidator 指定された正規表現にマッチするか検証します。
RequiredValidator 必須検証します。
TokenValidator 2重サブミットを検証します。
定義済みバリデーションクラス一覧

詳細はAPIドキュメントをご覧下さい。

バリデーションのエラーメッセージ

バリデーションのエラーメッセージは以下のルールで作成されます。

  • メッセージキーを指定しない場合、パラメータ名がエラーメッセージ中の項目名のメッセージキーとして使用されます。
  • メッセージキーを指定する場合、指定されたメッセージキーがエラーメッセージ中の項目名のメッセージキーとして使用されます。
  • DefaultValidationRules のコンストラクタでキーの項目名のメッセージキーのプリフィックスを指定することができます。
  • 各バリデーションのコンストラクタでメッセージキーを指定することで検証時のエラーメッセージを切り替えることもできます。
public class LoginAction extends Action {

  // 項目のメッセージキーを指定
  public ValidationRules validation = new DefaultValidationRules() {
    @Override
    public void initialize() {
      // メッセージキー"userId"の値がエラーメッセージ中の項目名として使用されます。
      add("userId", new RequiredValidator(), new MaxLengthValidator(10));
      // メッセージキー"login.password"の値がエラーメッセージ中の項目名として使用されます。
      add("password", "login.password", new RequiredValidator(), new MaxLengthValidator(10));
    }
  };

  // 項目名のメッセージキーのプリフィックスを指定
  public ValidationRules validation2 = new DefaultValidationRules("login.") {
    @Override
    public void initialize() {
      // メッセージキー"login.userId"の値がエラーメッセージ中の項目名として使用されます。
      add("userId", new RequiredValidator(), new MaxLengthValidator(10));
    }
  };

  // 各バリデーションにメッセージキーを指定
  public ValidationRules validation3 = new DefaultValidationRules() {
    @Override
    public void initialize() {
      // メッセージキー"err.myrequired"がエラーメッセージとして使用されます。
      add("password", new RequiredValidator("err.myrequired"));
    }
  };

  ...
}

バリデーションルールのコピー

ほとんどが同じでいくつかの項目が異なる複数のルールを作りたい場合、以下のように他のルールをコピーして使用することができます。

public class LoginAction extends Action {

  // ベースのルール
  private ValidationRules baseValidationRules = new DefaultValidationRules() {
    @Override
    public void initialize() {
      add("userId", new RequiredValidator(), new MaxLengthValidator(10));
      add("password", "login.password", new RequiredValidator(), new MaxLengthValidator(10));
    }
  };

  // ベースのルールに「nickname」の検証を追加
  public ValidationRules validationRules1 = new DefaultValidationRules() {
    @Override
    public void initialize() {
      // baseValidationRulesのルールをコピーして追加
      addAll(baseValidationRules);
      add("nickname", new RequiredValidator(), new MaxLengthValidator(10));
    }
  };

  // ベースのルールに「password2」の検証を追加
  public ValidationRules validation2 = new DefaultValidationRules() {
    @Override
    public void initialize() {
      // baseValidationRulesのルールをコピーして追加
      addAll(baseValidationRules);
      add("password2", new RequiredValidator(), new MaxLengthValidator(10));
    }
  };
  ...
}

アクションメソッドからエラーページへの遷移

アクションメソッドでエラーを検出した場合は ValidationException をスローすることで、処理を中断してエラーページへ遷移させることができます。

エラー発生時の挙動

DefaultValidationRules はバリデーションエラーが検出された場合にエラーページへフォワードします。 この挙動はDefaultValidationRules.html#fail(java.lang.String)メソッドに定義されています。 これをオーバーライドすることでエラー発生時の挙動を変更することができます。

  public ValidationRules validationRules = new DefaultValidationRules() {
    @Override
    public void initialize() {
      ...
    }

    @Override
    public void fail(String errorPage) {
      // クライアントに 404 Not Found を返す
      return new SendError(404);
    }
  };

独自バリデーションの定義

リクエストパラメータのバリデーション

リクエストパラメータのバリデーションを独自に作成するにはScalarFieldValidator またはArrayFieldValidatorを実装して作成します。
実装の方法はそれぞれのインターフェイスの実装クラスを参照してください。

ユーザー作成のロジックによるバリデーション

データベースアクセスなどのユーザー作成のロジックによるバリデーションを作成するにはValidationRuleを実装して作成してください。

バリデーションルールを登録する際には add メソッドの第一引数でバリデーションを実行するフェーズを指定できます。

バリデーションはフェーズごとに実行されます。 あるフェーズのバリデーションでエラーがあった場合は次のフェーズのバリデーションを実行せずにエラー処理へ移行します。

デフォルトでは DATA_TYPE(データ型を検証するフェーズ)DATA_CONSTRAINT(データ上の制約を検証するフェーズ) が定義されていて、 フェーズを指定しない場合は DATA_TYPE のフェーズに追加されます。

  public UserDao userDao;

  public String userId;
  public String password;
  public Map<String, Object> sessionScope;

  public ValidationRules loginValidation = new DefaultValidationRules(
      "login.") {
    @Override
    public void initialize() {
      add("userId", new RequiredValidator());
      add("password", new RequiredValidator());
      // userId と password の双方が入力されたことが確認されてから UserValidationRule
      // を実行したいので、フェーズに DATA_CONSTRAINT に指定します。
      add(DATA_CONSTRAINT, new UserValidationRule());
    }
  };

  private class UserValidationRule implements ValidationRule {

    public void apply(Map<String, Object[]> params, Object form,
        ActionErrors errors) throws ValidationException {
      User user = userDao.findByIdAndPassword(userId, password);
      if (user == null) {
        // エラーを検出した時、次のバリデーションを実行しないようにするためには
        // ValidationException をスローしてください。
        throw new ValidationException("ユーザIDかパスワードが違います。", "userId", "password");
      }
      sessionScope.put("user", user);
    }

  }

OVal プラグイン

Cubby のバリデーションはリクエストパラメータの名前に対するものなので、基本的にタイプセーフではありません。 入力項目が多い場合など、制約をフィールドやメソッドへのアノテーションで記述するほうが便利なことも多いので、 OVal - the object validation framework と連携してアノテーションベースのバリデーションを行う Cubby OVal Plug-in を用意しています。

このプラグインを使うと、リクエストパラメータがバインドされた後のフォームオブジェクトが OVal によって検証されます。

OVal プラグインの使用方法

  1. クラスパスに cubby-oval*.jar を追加してください。
  2. コンテナから net.sf.oval.localization.message.MessageResolver をキーに org.seasar.cubby.plugins.oval.validation.RequestLocaleMessageResolver を、 http://oval.sourceforge.net/api/net/sf/oval/localization/context/OValContextRenderer.html}net.sf.oval.localization.context.OValContextRenderer をキーに org.seasar.cubby.plugins.oval.validation.RequestLocaleOValContextRenderer を、それぞれ取得できるように設定してください。
  3. アクションクラスに ValidationRules を定義するとき org.seasar.cubby.plugins.oval.validation.OValValidationRule を追加してください。
  4. フォームオブジェクトとなるクラスに OVal のアノテーションで制約を記述してください。
    public class LoginAction extends Action {
    
      // バリデーションのルール
      public ValidationRules validationRules = new DefaultValidationRules() {
        @Override
        public void initialize(String resourceKeyPrefix) {
          //// フィールド "userId" は必須入力で最大10文字まで
          //add("userId", new RequiredValidator(), new MaxLengthValidator(10));
          //// フィールド "password" は必須入力で最大10文字まで
          //add("password", new RequiredValidator(), new MaxLengthValidator(10));
          add(new OValValidationRule(resourcekeyPrefix));
        }
      };
    
      @RequestParameter
      @NotNull
      @NotEmpty
      @MaxLength(10)
      private String userId;
    
      @RequestParameter
      @NotNull
      @NotEmpty
      @MaxLength(10);
      private String password
    
      @Validation(rules = "validationRules", errorPage = "confirm.jsp")
      public ActionResult save1() {
        ... 
      }