ブラウザなどの HTTP クライアントは、フォーム送信などのイベントのあと、フォームデータを Web アプリケーションに送信します。HTTP は、フォームデータを表すキーと値のペアに文字列型以外の型の使用を禁止しています。このため、サーバー側アプリケーションからは、フォーム送信データは一群の文字列オブジェクトと映ります。これは、データに関して特有の要件がある Web アプリケーションには問題になることがあります。妥当性検査とは、クライアントから送信されたフォームデータがデータ形式に従っていること、あるいはアプリケーションが求める特定の範囲内にあることを検査する処理です。
妥当性検査されるデータには、以下のようなものがあります。
妥当性検査は、JavaScript[tm] テクノロジを使用するなどのクライアント側スクリプトによる妥当性検査機能を提供するブラウザなどのクライアントでも行うことができます。しかしながら、クライアント側スクリプトが実行されるかどうかに関係なく、サーバー側妥当性検査を常に行うようにすることを推奨します。
妥当性検査を行うことによって、Web アプリケーションの安全性を高めることができます。妥当性検査は、クライアントから直接送信されてきた入力データが、そのままデータベースコマンドや出力される HTML 応答、シェルコマンドの実行で使用されないようにします。これらの方法は、Web アプリケーションが実行されているシステムを不正に制御する目的でよく使われます。
JavaServer[tm] Faces テクノロジは、開発者ばかりでなくページの作成者が、組み込まれている妥当性検査機能に加えて、固有の対処法を使用してサーバー側妥当性検査を定義する複数の方法を提供します。このドキュメントでは、JavaServer Faces テクノロジを使用してサーバー側妥当性検査を行う方法を紹介します。
対処法としては、JavaServer Faces テクノロジに組み込まれている機能を使用して、サーバー側フォームデータの妥当性検査を実現します。具体的には、JavaServer Faces テクノロジで利用できる、以下に示すような対処法について説明します。
以下では、それぞれの方法について詳しく説明します。次の表は、各対処法の長短をまとめています。
対処法 | 長所 | 短所 |
内蔵妥当性検査 | ページ作成者にとって設定が容易。 | 提供されている妥当性検査が限られる |
カスタム妥当性検査 | UI コンポーネントへの追加が容易。1 つのクラスで妥当性検査コードが定義される。 | ページ作成者が妥当性検査パラメータを設定できない。妥当性検査を変更するには、Java[tm] ソースを変更する必要がある。 |
カスタマイズ可能な妥当性検査パラメータを持つカスタム妥当性検査 | ページ作成者にとって設定が容易。javax.faces.webapp.ValidatorTag を拡張したクラスを利用して、カスタム妥当性検査が使用する妥当性検査パラメータのきめの細かい設定が可能。基本的に妥当性検査コードは、再利用可能なコンポーネントに変換される。 |
開発者にとって作成が複雑 (カスタムアクション、JavaServer Faces テクノロジの細部の知識が必要)。入力パラメータを妥当性検査するための Java コードが必要で、カスタマイズの自由度が低下する。 |
管理対象 Bean による妥当性検査 | ページ作成者にとって設定が容易。妥当性検査コードを、管理対象 Bean で簡単に定義可能。管理対象 Bean として設定可能なきめの粗い (アプリケーション全体) 妥当性検査に向いている。 | ページ作成者が、UI コンポーネントごとに妥当性検査を設定できない。妥当性検査コードのカスタマイズに複数メソッドが必要。 |
開発者は、実際のユースケースおよび上記の表に示されている短所に基づいて最善の対処法を選択します。
要約すると、以下のようになります。
JavaServer Faces テクノロジは、JavaServer Faces 開発者やサードパーティが、JavaServer Faces 実装に用意されているもの以外の包括的な一群の妥当性検査を提供するのに使用可能なモデルを提供します。それら妥当性検査は、再利用可能なライブラリとして提供できます。もう 1 つ検討すべきなのは、アプリケーションのニーズに合う場合はそうしたライブラリを使用することです。
以下では、各対処法をさらに詳しく説明します。
JavaServer Faces テクノロジには、float、long、あるいは integer といった数値の範囲検査などの基本的な検査を行うための妥当性検査がいくつか内蔵されています。これらの妥当性検査は、管理対象 Bean として初期化、設定されます。
内蔵妥当性検査の使用には、開発者が注意すべき副作用があります。少なくとも 1 つの妥当性検査エラーがあるフォームをユーザーが送信した場合、そのビューは再描画されて、エラーメッセージが表示されます。ただし、JavaServer Faces ライフサイクルの「モデル値更新」および「アプリケーション呼び出し」フェーズは省略されて、ただちに応答が描画されます。これには、不正と判定された値を使ってバッキング Bean が設定されるのを防ぐ目的があります。しかしながら、このことはまた、バッキング Bean が要求スコープ内にあって、妥当性検査エラーが発生した場合、ページ内の値バインドがすべてスキップされ、バッキング Bean がしかるべく更新されないことを意味します。たとえば非表示のテキストフィールドがバッキング Bean の文字列型プロパティー x
にバインドされている場合、妥当性検査エラーがあると、x
は更新されません。ほかの取得メソッドが x
の設定に依存している場合、応答は不完全なデータセットから描画されることになります。
JavaServer Faces テクノロジ 1.1 に付属している妥当性検査は以下のとおりです。
タグ名 | クラス名 | 説明 |
<f:validateDoubleRange> | javax.faces.validator.DoubleRangeValidator | double 型を使用する範囲の検査。通貨の検査に適している。 |
<f:validateLongRange> | javax.faces.validator.LongRangeValidator | long 型を使用する範囲の妥当性検査。 |
<f:validateLength> | javax.faces.validator.LengthValidator | フォーム変数の長さの妥当性検査。 |
妥当性検査を使用する方法は 2 通りあります。管理対象 Bean として定義、設定する方法と、対応するタグを使用して定義する方法です。
下記は、ページ作成者が内蔵妥当性検査タグの 1 つを定義、設定している例です。
<h:form id="validatorForm" onsubmit="return client_validation();">
<h:inputText id="bigNumber" value="#{ValidatorBean.bigNumber}">
<f:validateLongRange maximum="11" minimum="5"/>
</h:inputText>
<p>
<h:commandButton id="submit" action="success" value="Submit" />
</p>
<p>
<h:message style="color: orange" id="error3" for="bigNumber"/>
</p>
</h:form>
InputText タグのサブ要素は、数値の範囲を検査します。この場合、妥当性検査に対するパラメータはタグの属性 (最大/最小) として設定します。数値が不正な場合は、<h:message> 要素の位置にエラーメッセージが描画されて、ページが再表示されます。
内蔵妥当性検査は、きめの細かい UI コンポーネント妥当性検査を提供することができます。値バインドを利用して、内蔵妥当性検査の属性を設定することもできます。次のように妥当性検査を指定することによって、上記の例を拡張することもできます。
<f:validateLongRange minimum="#{MyBackingBean.minimum}" maximum="#{MyBackingBean.maximum}" />
この例では、MyBackingBean というバッキング Bean に範囲属性を指定しています。
UI コンポーネントで、必須 属性を true
と定義し、null または長さゼロの文字列型値が定義されても、妥当性検査が呼び出されるようにすることができます。ただし、妥当性検査コードがそうした値を受け付け可能である必要があります。必須属性が true
に設定されていない場合、UI コンポーネントに登録されている妥当性検査は呼び出されません。
内蔵の妥当性検査が、アプリケーション全体の妥当性検査を提供するように定義することもできます。この場合、妥当性検査は管理対象 Bean として定義、初期化されます。このように定義、初期化された管理対象 Bean は、管理対象 Bean に定義したカスタム妥当性検査を使って行うのと同じ方法で妥当性検査の属性に対してメソッドバインドを使用し、UI コンポーネントの内容を検査することができます。以下は、内蔵妥当性検査の 1 つを使用するアプリケーションスコープの妥当性検査の管理対象 Bean の例です。
<managed-bean>
<description>
A Pre-defined JavaServer Faces Long Range Validator
</description>
<managed-bean-name>JSFValidator</managed-bean-name>
<managed-bean-class>javax.faces.validator.LongRangeValidator</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
<managed-property>
<property-name>minimum</property-name>
<property-class>int</property-class>
<value>0</value>
</managed-property>
<managed-property>
<property-name>maximum</property-name>
<property-class>int</property-class>
<value>5</value>
</managed-property>
</managed-bean>
JSFValidator のこの管理対象 Bean 定義は、管理対象 Bean として妥当性検査の 1 つを指定することによって定義される、内蔵の範囲検査の妥当性検査を使用します。また、この JSFValidator Bean には、最小および最大の範囲が設定されています。
<h:form id="validatorForm">
<p>Enter a number (between 0-5):</p>
<h:inputText id="number" value="#{ValidatorBean.number}"
validator="#{JSFValidator.validate}"/>
<p>
<h:message style="color: orange" id="errors2" for="number"/>
</p>
<p>
<h:commandButton id="submit" action="success" value="Submit" />
</p>
</h:form>
上記のコードでは、内蔵妥当性検査を (この場合は JSFValidator という名前) を使用して、提供された番号の値を検査します。番号が範囲内にない場合は、エラーメッセージの入ったページが再表示されます。
管理対象 Bean メソッドによる妥当性検査
管理対象 Bean またはバッキング Bean に定義されたメソッドとしてメソッド妥当性検査を行うことができます。メソッドのバインドは、UI コンポーネントの妥当性検査属性を使用してマッピングされます。
<f:view>
<h:form id="validatorForm" >
<p>
Enter a new name (other than "blueprints").
</p>
<h:inputText id="userName" value="#{ValidatorBean.userName}"
validator="#{ValidatorBean.validate}"/>
<h:commandButton id="submit" action="success" value="Submit" />
<p>
<h:message style="color: red"
id="errors1"
for="userName"/>
</h:form>
</f:view>
上記の JavaServer Faces ページの抜粋は、ValidatorBean という管理対象 Bean 内の validate というメソッドを使用する入力テキストフィールドを示しています。妥当性検査がマッピングされているこのメソッドは、特定のメソッド署名に従っている必要があります。JavaServer Faces 実行環境は、このメソッドに必要な値を供給します。開発者は、入力を妥当性検査するためのコードを用意すればよいだけです。
public void validate(FacesContext context,
UIComponent component,
Object value) throws ValidatorException {
if ((context == null) || (component == null)) {
throw new NullPointerException();
}
if (value != null) {
if (((String)value).equals("blueprints")) {
throw new ValidatorException(new FacesMessage("blueprints is invalid"));
}
}
}
上記のコード抜粋は、UIComponent の妥当性検査属性にバインドされたメソッドのメソッド署名を示しています。この UIComponent は、FacesContext と自己参照、妥当性検査する値をメソッドに供給します。妥当性検査エラーが検出されたことを示す ValidatorException 例外をスローするには、このメソッドが必要です。
上図は、カスタム妥当性検査例外が発生したときの動作を示しています。この場合、ValidatorException という妥当性検査エラーがスローされると、JSF ページが再表示され、その例外で提供されたエラーメッセージが表示されます。
一般に、妥当性検査メソッドは、そのメソッドを参照するコンポーネントのプロパティーを定義しているのと同じ管理対象 Bean に含める方が優れています。これは、イベントの処理方法あるいはコンポーネントに関連付けられている妥当性検査を行う方法を決定するにあたって、メソッドによるコンポーネントのデータへのアクセスが必要になることがあるためです。
Java コードへのエラーメッセージのハードコーディングを避けるには、Java[tm] 2 SDK の ResourceBundle 機能 (java.util.ResourceBundle) の使用を推奨します。この機能を使用することによって、エラーメッセージをローカライズにすることもできます。JavaServer Faces テクノロジにはリソースバンドル機能が用意されていますが、これまでと変わらず、メッセージの書式を適切にするのは、開発者の役割です。
Java のコーディング知識を持っている開発者には、管理対象 Bean で妥当性検査コードをメソッドとして定義するのは容易です。この場合、妥当性検査は、特定の UI コンポーネントまたは管理対象 Bean に結びつけられます。管理対象 Bean の設定で多少はカスタマイズ可能ですが、管理対象 Bean の妥当性検査には制約があります。
カスタム妥当性検査を用いる方法は、管理対象 Bean を用いる方法に似ています。メソッド署名は同じですが、カスタム妥当性検査の場合、メソッド名は validate
になります。開発者は、次のことを行う必要があります。
javax.faces.validator.Validator
インタフェースを実装しているクラスを作成する。コンポーネントの状態をクライアントに保存する場合は、javax.faces.component.StateHolder
インタフェースも実装するようにします。 <validator>
<description>
Registers the Validator implementation,
DateValidator.
</description>
<validator-id>DateValidator</validator-id>
<validator-class>com.DateValidator</validator-class>
</validator>
JavaServer Faces ページでは、javax.faces.component.EditableValueHolder
インタフェースを実装している UI コンポーネントにこの妥当性検査を関連付けます。以下は、DateValidator
を使用している <h:inputText> タグの例です。
<h:inputText id="date1" value="11-12-99">
<f:validator validatorId="DateValidator"/>
</h:inputText>
javax.faces.validator.Validator
インタフェースを実装している、この妥当性検査は、直下のサブ要素 <f:validator validatorId="DateValidator"/>
を使って UI コンポーネントにマッピングされます。妥当性検査クラスの validate メソッドには、以下のような妥当性検査ロジックが含まれています。
public void validate(FacesContext context, UI component component,
Object inputValue) {
boolean valid = false;
String value = (String)inputValue;
try {
DateFormat df = new SimpleDateFormat("mm-dd-yy");
Date d = df.parse(value);
if (d != null) valid = true;
} catch (java.text.ParseException px) {
valid = false;
} catch (java.lang.IllegalArgumentException iae) {
valid = false;
}
if (!valid) {
FacesMessage error = new FacesMessage("Date " + value +
" is invalid. Please enter a date conforming to " +
datePattern);
throw new ValidatorException(error);
}
}
メソッド署名が管理対象 Bean の妥当性検査メソッドに使用されいるものと同じであることに注意してください。
妥当性検査クラスによる方法を用いることの最大の難点は、ページ作成者が UI コンポーネントごとに妥当性検査パラメータを設定できないことです。<c:set> タグを使って管理対象 Bean に値を設定することはできますが、管理対象 Bean の仕組みや JSF ライフサイクルモデルに関する細部にわたる知識が必要なため、簡単な作業ではありません。また、妥当性検査パラメータ (たとえば、上記の例の日付パターン "mm-dd-yy") は、妥当性検査クラスにハードコーディングすることができます。その場合、その値は、ロケールに固有の java.util.ResourceBundle
クラスまたはプロパティーファイルに保存することができます。
この方法は Web アプリケーション全体のグローバル妥当性検査の提供に適していますが、妥当性検査パラメータを変更する段階になると、融通性がありません。
カスタマイズ可能な妥当性検査パラメータを持つカスタム妥当性検査
開発者は、javax.faces.webapp.ValidatorTag
を拡張したカスタムアクションを作成することによって、妥当性検査実装クラスにカスタマイズ可能なパラメータを提供することができます。妥当性検査に使用するパラメータは、コンポーネントごとに JSF ページでプロパティーバインド表現を使用して設定することもできます。
開発者は次のことを行います。
javax.faces.validator.Validator
インタフェースを実装しているクラスを作成する。コンポーネントの状態をクライアントに保存する場合は、javax.faces.component.StateHolder
インタフェースも実装するようにします。 javax.faces.webapp.ValidatorTag
を拡張したクラスを作成する。 javax.faces.webapp.ValidatorTag
を拡張したクラスによって設定されたパラメータを受け取り、妥当性検査を行う、javax.faces.webapp.Validator
拡張妥当性検査クラスを作成する。
上図は、パラメータのカスタマイズが可能な JavaServer Faces 妥当性検査を作成するために必要なアーティファクトを表しています。この妥当性検査は正規表現を使用して、日付パターンを指定します。
JavaServer Faces 実行環境は、ページが処理されるときに DateValidatorTag
を初期化します。DateValidatorTag
は DateValidator
のインスタンスを作成し、ページに指定されていた内容に基づいてパラメータを設定します。以下は、この DateValidator
validate メソッドを示しています。
// 初期化時に DateValidator タグによって呼び出される
public void setDatePattern(String datePattern) {
this.datePattern = datePattern;
}
public void validate(FacesContext context, UIComponent component,
Object inputValue) {
boolean valid = false;
String value = (String)inputValue;
try {
valid = Pattern.matches(datePattern, value);
} catch(PatternSyntaxException pse) {
// 表現に問題がある場合
}
if (!valid) {
FacesMessage error = new FacesMessage("Date " + value +
" is invalid. Please enter a date conforming to " +
datePattern);
throw new ValidatorException(error);
}
}
このコードは、カスタムアクションを使って datePattern が設定された妥当性検査の validate メソッドを示しています。この方法により、ページ開発者は UI コンポーネントごとに datePattern を定義できます。提供された値 (この例の inputValue
) にエラーが含まれる場合は、ValidationException
がスローされて、そのことが示されます。
カスタマイズ可能な妥当性検査では、開発者が行わなければならない作業が多少増えます。開発者は、適切なタグライブラリ記述子ファイルを用意するほかに、javax.faces.webapp.Validator
を拡張するカスタムアクションも開発するべきです。この方法の利点は、妥当性検査を使用して、UI コンポーネントのきめの細かい妥当性検査を行えることです。カスタムアクションを使用する妥当性検査の開発は簡単ではありませんが、ページ作成者が使用するのは容易です。
この項目に関する詳細は、次の資料を参照してください。