Name
Password

バリデーション

  • 2009-09-03 11:37:58

Pageコンポーネントのアクション呼び出しの際にリクエストパラメータが正当であるかどうかを検証することができます。この処理を「パラメータチェック」または「バリデーション」と呼びます。Ymirではバリデーションは「制約チェック」の一種です(他の制約チェックの種類としては「権限チェック」があります)。

バリデーションの指定

バリデーションを指定するにはアノテーションを用います。詳細については「制約の指定」を参照して下さい。

処理の詳細

バリデーションはPageコンポーネントへの依存コンポーネントやリクエストパラメータがセットされた後、アクション呼び出しの前に行なわれます。バリデーションに失敗すると、アクションを呼び出す代わりに以下の処理が行なわれます。

  1. バリデーション失敗に関する詳細情報であるorg.seasar.ymir.message.Notesオブジェクトが「notes」というキーでHttpServletRequestオブジェクトのアトリビュートとしてセットされる。
  2. Pageコンポーネントが_validationFailedメソッドを持っている場合、それが呼び出される。
  3. _prerenderメソッドが呼び出される。

_validationFailedメソッドが引数を持つ場合は引数が解決されます。この時Notesオブジェクトがデフォルトの引数値として使用されます。メソッドの引数の解決については「アクションメソッドの引数値の解決」を参照して下さい。

Notesオブジェクト

バリデーションに失敗した場合、その情報がorg.seasar.ymir.message.Notesオブジェクトにセットされます。バリデーションのためのアノテーションが複数指定されている場合は全てのバリデーションが行なわれ、失敗したものについてNotesオブジェクトに情報がセットされます。

Notesオブジェクトは1つまたは複数のorg.seasar.ymir.message.Noteオブジェクトをカテゴリ毎に保持しています。Noteオブジェクトはvalueとパラメータを保持しています。StrutsのActionMessagesとActionMessageと同じようなものと思っていただければいいと思います。

Note#getValue()の値(noteキー)はバリデーションの種類によって異なりますので、これを見ることで失敗したバリデーションが何か知ることができます。

バリデーション失敗に関するNoteオブジェクトは、そのバリデーションの対象であるリクエストパラメータ名と同じ名前のカテゴリに関連付けられてNotesオブジェクトに追加されます。

メッセージの表示

バリデーションエラーメッセージを表示するには、例えば以下のようにします。

まとめて表示する場合、

<ul tal:condition="notes">
  <li tal:repeat="note notes/notes" tal:content="note/%value">MESSAGE</li>
</ul>

全体に関するメッセージを先頭に表示し、項目ごとのメッセージ項目ごとに表示する場合、

<ul tal:condition="notes/size(org.seasar.ymir.message.GLOBAL_NOTE)">
  <li tal:repeat="note notes/org.seasar.ymir.message.GLOBAL_NOTE" tal:content="note/%value">GLOBAL MESSAGE</li>
</ul>
...
<ul tal:condition="notes/size(param)">
  <li tal:repeat="note notes/param" tal:content="note/%value">MESSAGE FOR PARAM</li>
</ul>

メッセージを表すNoteオブジェクトがどのように表示されるかについてはNotesオブジェクトを使ったメッセージ文字列の構築を参照して下さい。

メッセージのカスタマイズ

表示されるメッセージをカスタマイズするには、アプリケーションのsrc/main/resourcesにあるmessages.xpropertiesに書かれているプロパティのうち、noteキーと同じキーを持つものを変更して下さい。

xpropertiesファイルの書式は基本的に通常のJavaのプロパティファイルと同じですが、UTF-8で直接非ASCII文字を記述することができます。

国際化対応のアプリケーションの場合は各言語用のmessages_*.xpropertiesを変更して下さい。

バリデーションエラーの対象となったパラメータはそのまま表示されますが、パラメータの表示文字列を変更したい場合はxpropertiesファイルに「label.パラメータ名」というエントリを追加して下さい。例えば「name」というパラメータの表示を「なまえ」にしたい場合はmessages_ja.xproperties(国際化を意識しないアプリケーションの場合はmessages.xpropertiesでも構いません)に以下のようなエントリを追加して下さい:

label.name=なまえ

バリデーションの種類

Ymirが標準で提供しているバリデーションは以下のとおりです。(アノテーションクラスはorg.seasar.ymir.constraint.annotationパッケージ等にあります。)

アノテーション名 説明
Confirmed

指定されたリクエストパラメータの値が全て一致していることを指定します。

制約の対象となるのは、valueプロパティで指定されたリクエストパラメータと、アノテーションが付与されているSetterから導き出せるプロパティ名と同名のリクエストパラメータです。

この制約は、例えば確認のためにメールアドレスを2回入力させるようなフォームを処理するために利用することができます。

noteキーはerror.constraint.confirmedです。

指定できるプロパティは以下のとおりです。

value
対象となるリクエストパラメータ名。複数指定可能。
Datetime

リクエストパラメータの値が日付パターンにマッチしていることを指定します。

指定されている全てのリクエストパラメータの値が、アノテーションのプロパティとして指定された日付パターンに全体マッチすることを要求します。

制約の対象となるのは、propertyプロパティで指定されたリクエストパラメータと、アノテーションが付与されているSetterから導き出せるプロパティ名と同名のリクエストパラメータです。

noteキーはerror.constraint.datetimeです。

指定できるプロパティは以下のとおりです。

property
対象となるリクエストパラメータ名。複数指定可能。
value
日付のパターン。このプロパティはpatternプロパティと同じです。patternプロパティとともに指定された場合はpatternプロパティが優先されます。
pattern
日付のパターン。書式はSimpleDateFormatクラスの日付パターンと同じです。パターンを省略した場合のデフォルトパターンは「yyyy-MM-dd HH:mm:ss」です。
Length

リクエストパラメータ文字列の長さに関する制約を指定します。

制約の対象となるのは、propertyプロパティで指定されたリクエストパラメータと、アノテーションが付与されているSetterから導き出せるプロパティ名と同名のリクエストパラメータです。

noteキーはerror.constraint.lengthです。

指定できるプロパティは以下のとおりです。

property
対象となるリクエストパラメータ名。複数指定可能。
min
長さがここで指定したのと同じか長い必要があることを表します。
max, value
長さがここで指定したのと同じか短い必要があることを表します。valueよりもmaxの方が優先されます。
Matched

リクエストパラメータの値が指定された正規表現パターンにマッチしているという制約を指定します。

制約の対象となるのは、propertyプロパティで指定されたリクエストパラメータと、アノテーションが付与されているSetterから導き出せるプロパティ名と同名のリクエストパラメータです。

noteキーは、メッセージキー(後述)を指定した場合はerror.constraint.matched.[メッセージキー]です。メッセージキーを指定しなかった場合はerror.constraint.matchedです。

指定できるプロパティは以下のとおりです。

property
対象となるリクエストパラメータ名。複数指定可能。
value
正規表現のパターン。このプロパティはpatternプロパティと同じです。patternプロパティとともに指定された場合はpatternプロパティが優先されます。
pattern
正規表現のパターン。
messageKey
メッセージキー。この指定を使うことでエラーメッセージの文字列を自由に指定することができます。
Numeric

リクエストパラメータが数値として解釈可能であるという制約を指定します。

制約の対象となるのは、valueプロパティとpropertyプロパティで指定されたリクエストパラメータと、アノテーションが付与されているSetterから導き出せるプロパティ名と同名のリクエストパラメータです。

noteキーはerror.constraint.numericです。

指定できるプロパティは以下のとおりです。

value
対象となるリクエストパラメータ名。複数指定可能。
property
対象となるリクエストパラメータ名。複数指定可能。
integer
値が整数値である必要があることを表します。デフォルトの値はtrueです。
greaterThan
値がここで指定したよりも大きい必要があることを表します。
greaterEqual
値がここで指定したのと同じか大きい必要があることを表します。
lessThan
値がここで指定したよりも小さい必要があることを表します。
lessEqual
値がここで指定したのと同じか小さい必要があることを表します。
Required

リクエストパラメータが必須である(nullや空文字列ではない)という制約を表します。

制約の対象となるのは、valueプロパティで指定されたリクエストパラメータと、アノテーションが付与されているSetterから導き出せるプロパティ名と同名のリクエストパラメータです。

noteキーはerror.constraint.requiredです。

指定できるプロパティは以下のとおりです。

value
対象となるリクエストパラメータ名。複数指定可能。
completely
同一名のパラメータが複数指定されている場合に、全てのパラメータが埋まっているべきかどうか。デフォルトはfalse。falseの場合は、複数のうちどれか1つだけでも埋まっていればバリデーションエラーになりません。
allowWhitespace
半角空白文字を空とみなさないかどうか。デフォルトはtrue。このプロパティがtrueである場合は、例えば半角空白だけからなる文字列を空とみなしません。falseである場合は、半角空白だけからなる文字列を空とみなします。
allowFullWidthWhitespace [Ymir-1.0.5以降]
全角空白文字を空とみなさないかどうか。デフォルトはtrue。このプロパティがtrueである場合は、例えば全角空白だけからなる文字列を空とみなしません。falseである場合は、全角空白だけからなる文字列を空とみなします。このプロパティがfalseでかつallowWhitespaceもfalseの場合は、半角空白と全角空白だけからなる文字列を空とみなします。
TokenRequired

リクエストパラメータとして渡されたトランザクショントークンがセッションに保持されているものと一致する必要があることを表します。

このアノテーションはクラスまたはアクションメソッドに付与して下さい。

noteキーはerror.constraint.token.tokenRequiredです。

指定できるプロパティは以下のとおりです。

value
トークンを保持しているリクエストパラメータ名。省略した場合はデフォルトの名前が参照されます。

ページ固有のバリデーション

独自にバリデーションのためのアノテーションと処理クラスを作成することでアプリケーション固有のバリデーションを実現することができますが、ページ固有のバリデーションの場合はわざわざアノテーションと処理クラスを作るのは大変です。そこでYmirでは、ページ固有のバリデーションをPageクラス内に書くことができるようになっています。

具体的には、返り値がorg.seasar.ymir.message.Notesであるようなメソッドまたはorg.seasar.ymir.constraint.ValidationFailedExceptionをスローするような返り値がvoidであるメソッドをPageクラスに追加して、org.seasar.ymir.constraint.annotation.Validatorアノテーションを付与することで、そのメソッドがバリデーションの際に呼び出されるようになります。

形式1:

@Validator
public Notes validate()
{
    if ([バリデーション失敗]) {
        return new Notes().add([リクエストパラメータ名], new Note(
            Constraint.PREFIX_MESSAGEKEY + [バリデート種別],
            new Object[]{ [リクエストパラメータ名] }));
    } else {
        return null;
    }
}

形式2:

@Validator
public void validate() throws ValidationFailedException
{
    if ([バリデーション失敗]) {
        throw new ValidationFailedException(
            new Notes().add([リクエストパラメータ名], new Note(
                Constraint.PREFIX_MESSAGEKEY + [バリデート種別],
                new Object[]{ [リクエストパラメータ名] })));
    }
}

バリデーションに失敗した場合は、返り値またはValidationFailedExceptionのコンストラクタの引数として、失敗情報に対応するNoteオブジェクトを含むようなNotesオブジェクトを指定するようにして下さい。バリデーションに成功した場合は、返り値がNotesのメソッドの場合は空のNotesオブジェクトを返すかnullを返すようにして下さい。

バリデータメソッドが引数を持つ場合は、引数の値が解決されます。この時、リクエストされたアクションがボタンに関連付けられている場合はボタン名に埋め込まれているパラメータがデフォルトの引数値として使用されます。メソッドの引数の解決については「アクションメソッドの引数値の解決」を参照して下さい。

例えばbutton[10][delete]というボタンが押されてPOSTリクエストが送られてきた場合、以下のメソッドのrequestにはHttpServletRequestオブジェクトが、idには10が、typeには"delete"が代入されます。

@Validator
public void validate(@Resolve HttpServletRequest request, int id, String type) {
    ....
}

@Validatorアノテーションは複数のメソッドに付与することができます。また、あるアクションが呼び出される時だけバリデーションメソッドを動作させたい場合は、アノテーションの引数としてアクションメソッド名を列挙して下さい。アクションメソッド名を指定しない場合は全てのアクションに関してバリデーションが有効になります。