No Struts

Struts1.3の入力バリデーション機能 Struts-Validatorを使う

2011年1月24日Apache Struts

本連載の内容は記事執筆当時のまま再公開しており、現在では古い、あるいは適切ではない内容を含んでいます。

ソリューション開発部の黒住です。

Webアプリケーションの製造において、避けて通れない大きな処理に、ユーザーの入力値検証があります。この入力値検証を、検証アルゴリズムの再利用とXMLによる集中管理により、効率よく実装する機能を提供するのが、Struts1.1から導入された Struts-Validatorです。このStruts-Validatorの使い方を、本稿ではStruts1.3を用いて紹介します。

Struts-Validatorとは

Webアプリケーションにおけるユーザー入力値の検証を効率よく実装する機能を提供してくれるStruts-Validatorについて、本章では、入力値検証における問題、Struts-Validatorの特徴、動作のしくみという流れで説明します。

入力値検証における問題

Webアプリケーションにおける入力値の検証は、ブラウザから送られてくる文字コードの違いや、検証のタイミング、検証ロジックの実装方法など、実にさまざまな問題を抱えています。Strutsでは従来からアクションフォームBeanのvalidateメソッドを用いた入力値の検証機能が提供されていました。この機能で単純な入力値の検証 (入力文字検証や構文検証) を行い、複雑な値の検証 (アカウントがDB上に存在するか?などの意味検証) はビジネスロジック層で行うというのが、Strutsを用いた代表的な入力値検証のポリシです。しかし、アクションフォームBeanのvalidateメソッドを用いた検証コードは開発者がアクションフォームBeanごとに実装する必要があり、検証のための冗長なコードが重複して記述される傾向があります。また、Struts1.1から導入された DynaActionForm (動的アクションフォーム。アクションフォームBeanを動的にStrutsが生成する) にはvalidateメソッドを実装する手段がなく、すべての検証をアクション (またはビジネスロジック層) にて行う必要があります。これら諸問題を解決するためにStruts-Validatorという機能がStruts1.1から提供されるようになりました。

Struts-Validatorの特徴

Struts-ValidatorはStrutsの検証機能を強化するための標準機能です。Struts-Validator自体はStrutsのコアとなるjarファイルに含まれていますが、これを用いずにアクションフォームBeanの入力検証機能のみを利用してアプリケーションを構築することは可能です。しかし、Struts-Validatorは非常に強力な機能を有しているため、ぜひ利用を検討してください。Struts-Validatorの特徴を以下に示します。

XMLベースの検証定義

Struts-Validatorは、アクションフォームBeanのvalidateメソッドを実装する代わりに、アクションフォームBeanの属性ごとの検証ルールをXMLフォーマットで定義します。こうすることで、検証定義を集約できるだけでなく、検証コードは別に作成し名前を付けて参照するため、検証コードの再利用性が高まります。

リクエスト地域や言語別に検証ルールの定義が可能

検証によっては、利用者が利用している地域によってルールが変わるものがあります。例えば郵便番号などが顕著な例でしょう。Struts-Validatorではロケールごとに検証を定義できるため、このようなケースにも柔軟に対応できるようになっています。

独自検証ルールの実装

いくつかの検証ルールがデフォルトで用意されていますが (「表:標準検証ルール」)、アプリケーションによっては、独自の検証が必要な場合があります (半角カナの入力チェックなど)。Struts-Validatorでは独自の検証のためのアルゴリズムを実装したクラスを用意し、検証ルール名を付ける方法が用意されています。この機能を利用すれば、デフォルトの検証ルールと同様の方法で、独自のルールを簡単に利用することができます。

メッセージリソースベースのエラーメッセージ出力

エラーメッセージの表示方式は、Strutsが提供しているメッセージリソースのしくみを利用しているため、メッセージを独自に管理する必要がなく、国際化にも当然対応していることになります。

動的アクションフォームに対する検証

Struts1.1から導入された動的アクションフォームBeanを、通常のアクションフォームBeanと同様に検証する機能を提供します。検証のための設定などに差はなく、動的アクションフォームBeanの短所であったアクションフォームBeanでの検証ができない、という問題をなくすことができます。

JavaScriptによるクライアントサイド検証

Strutsが用意している検証機能は、フォームの情報がサーバーに送られてきて初めて機能するものですが、Struts-Validatorには、クライアント検証の機能もあります。クライアントサイド検証を行った場合、検証用のJavaScriptコードが自動生成されてWebページに埋め込まれます。そして、フォームの情報がサーバーへ送信される前に入力検証が機能します。レスポンスが遅いサーバーや、通信線が細い場合に非常に有効な機能です。この機能は明示的に利用しなければ有効になりません。

標準検証ルール

検証ルール名検証内容
required必須入力チェック
requiredif条件付きのフィールド必須入力チェック
validwhen関連する複数のフィールドに対する入力チェック
minlength指定の長さ以上かをチェック
maxlength指定の長さ以下かをチェック
mask指定のマスクパターンに合致しているかチェック
byteByteクラスを生成できる文字列かをチェック
shortShortクラスを生成できる文字列かをチェック
integerIntegerクラスを生成できる文字列かをチェック
longLongクラスを生成できる文字列かをチェック
floatFloatクラスを生成できる文字列かをチェック
doubleDoubleクラスを生成できる文字列かをチェック
byteLocaleプリミティブ型のbyteに変換可能な文字列かをユーザーのロケールを考慮してチェック
shortLocaleプリミティブ型のshortに変換可能な文字列かをユーザーのロケールを考慮してチェック
integerLocaleプリミティブ型のintに変換可能な文字列かをユーザーのロケールを考慮してチェック
longLocaleプリミティブ型のlongに変換可能な文字列かをユーザーのロケールを考慮してチェック
floatLocaleプリミティブ型のfloatに変換可能な文字列かをユーザーのロケールを考慮してチェック
doubleLocaleプリミティブ型のdoubleに変換可能な文字列かをユーザーのロケールを考慮してチェック
dateDateクラスを生成できる文字列かをチェック
intRange指定した2つのInteger値の間にあるかをチェック
longRange指定した2つのLong値の間にあるかをチェック
floatRange指定した2つのFloat値の間にあるかをチェック
doubleRange指定した2つのDouble値の間にあるかをチェック
creditCard正当なクレジットカード番号のフォーマットかをチェック
email正当なメールアドレスのフォーマットかをチェック
url正当なURLのフォーマットかをチェック

Struts-Validatorの動作のしくみ

Struts-Validatorは、プラグインといくつかのアクションフォームBeanから構成されています。プラグインは、Struts-Validatorによる検証機能を有効にするためのものです。アクションフォームBeanはStruts-Validatorを呼び出すための機能をもったActionFormの継承クラスです。Struts-Validator利用時には、Struts-Validatorのプラグイン (クラス org.apache.struts.validator.ValidatorPlugIn) をStruts設定ファイル (struts-config.xml) に定義し、そのプラグインに検証を記述したXMLファイルを指定します。また、Struts-Validatorで検証を行うアクションフォームBeanは、Struts-Validatorが提供するアクションフォームBeanのクラスを継承する必要があります。これらの利用によって、図のようなイメージで検証が行われるのです。

説明図:Struts-Validator動作イメージ
図:Struts-Validator動作イメージ

アクションフォームBeanに対する検証定義を記述したXMLファイルは、Struts-Validator (のプラグイン) によって起動時に読み込まれます (図の 1 検証定義の読み込み)。WebブラウザからのHTTPリクエストはStrutsのコントローラー (ActionServletとリクエストプロセッサ) が受けます (図の 2 HTTPリクエスト)。その後、アクションフォームBeanにHTTPリクエストパラメーター (HTML入力フォームの値) をセットします (図の 3 値のセット)。ここまでは、通常のStrutsのアプリケーションと同様です。ただし、値がセットされるアクションフォームBeanは、Struts-Validatorが提供しているアクションフォームBeanを継承しており、入力検証機能の動作が異なります。通常であればアクションフォームBeanのvalidateメソッドをオーバーライドし、メソッド内に検証ロジックを実装していきますが、Struts-Validatorの検証機能を用いる場合にはオーバーライドは行いません。検証機能がStrutsより呼び出された場合、Struts-Validatorの検証機能が動作するようになっています (図の 4 値の検証)。検証は定義に従って行われます。検証に成功すると、通常のStrutsの動作と同様にアクションクラスが呼ばれます (図の 5 検証成功時、呼び出し)。失敗時には入力ページへ遷移するといった従来と同様の動作を行います。検証失敗時は、Strutsに定義したメッセージリソースより対応するエラーメッセージが選択され、StrutsのActionErrorsとして生成されるため、エラーメッセージの処理に関して、Struts-Validator専用の定義ファイルは必要としません。

Struts-Validatorの利用方法

では、早速Struts-Validatorを利用した入力検証を利用する方法を紹介しましょう。今回実装に利用するアプリケーションは、「Struts1.3によるWebアプリケーションの作り方 (概要編/構築編) 」で作成したLogonを行うアプリケーションです。

Struts-ValidatorアクションフォームBeanの利用

Struts-Validatorの検証を利用にするには、アプリケーション内の各アクションフォームBeanがStruts-Validatorの提供しているアクションフォームBeanのクラスを継承する必要があります。アクションフォームBeanの種類は複数提供されていますが、今回は一番基本的な ValidatorForm クラスを利用します。このクラスを利用した、ログオン入力フォームの情報を保持するクラス LogonForm は「リスト:LogonForm.java」のようになります。validateメソッドのオーバーライドを行っていないのがおわかりいただけますか。アクションフォームBeanの用意はこれだけです。

リスト:LogonForm.java
リスト:LogonForm.java

検証の定義

次にアクションフォームBeanの各属性 (HTML form の入力フィールド) と検証ルールの対応を定義したXMLファイルを用意する必要があります。アクションフォームBeanの各属性と検証ルールの対応を記述した検証定義ファイルは、今回はvalidation.xmlとして用意しました (ファイル名は任意)。このファイルにはリストのようにアクションフォームBeanごとに各属性と検証ルールの対応を定義していきます。

リスト:validation.xml
リスト:validation.xml

Struts1.3のStruts-Validatorはcommons-validator1.2の機能を利用して検証機能を実現しているため、「リスト:validation.xml」の記述はそれに従ったものとなっています。基本的なフォーマットは、アクションフォームBeanごとの検証定義を <form>エレメントに定義していくことになります。各<form>エレメントのname属性には、検証を定義するアクションフォームBeanの論理名 (Struts設定ファイルに定義) を指定します。「リスト:validation.xml」の 1 では logonFormを指定しています。<form>エレメントの内部にはネストさせて各属性に対する検証を定義していくことになります。アクションフォームBeanの各属性は<field>エレメントのproperty属性で名前を指定し、depends属性で検証ルールを定義していきます。「リスト:validation.xml」の 2 ではuserId属性に対して required標準検証ルールを指定していることになります。

<field>エレメントにネスト指定している <arg>エレメントは、この検証が失敗した場合に生成するエラーメッセージに関する定義をするエレメントです。それぞれの標準ルールにはエラーメッセージのキーが定義されており、requiredルールの場合はerrors.required、emailルールの場合、errors.emailといった具合にerrorsに続いてルール名の文字列をキーとして、メッセージリソース内から得るようになっています。よって、Struts-Validatorを利用する場合には、これらメッセージリソースを定義しておく必要があります。

その際に、文字列置き換えポイント (プレースフォルダ) を用意することで、動的にメッセージを変化させることができるようになっています。例えば、入力エラーのメッセージを出力する際には、「XXXフィールドは必須入力項目です」という具合にフィールド名を付けて表示するのが親切でしょう。このようなメッセージを生成するためにはXXXに当たる部分を動的に変更できれば、メッセージ定義を汎化することができます。このXXXの部分にプレースフォルダを {数字} の形式で定義することで、動的に文字列の置き換えが行えるのです。例えば、「{0}フィールドは必須入力です」というメッセージリソースの定義を行った場合、{0}の部分がプレースフォルダとなり、動的に変更可能な箇所となります。

そして、<arg>エレメントはこのプレースフォルダに対する置き換え文字列の指定を行うエレメントです。<arg>エレメントの指定は、置き換える文字列をメッセージリソースから取り出すようになっており、そのキーをkey属性で指定します。また、1つのメッセージ内のプレースフォルダは {数字} の形式で複数定義可能であり、position属性にはその数字を指定し、置き換え場所を指定するようになっています (position属性を省略した場合は 0 と解釈されます)。よって、「リスト:validation.xml」の 3 の指定で、メッセージリソースキーmessage.prompt.userId が「ユーザーID」と定義され、errors.requiredが「{0}フィールドは必修入力項目です」と定義されていた場合に生成されるエラーメッセージは、「ユーザーIDフィールドは必修入力項目です」となります。あとは、各属性に対して<field>エレメントにて検証を定義していけばよいわけです。今回利用しているrequiredルールに対するメッセージリソースは前述したように「errors.required={0}フィールドは必修入力項目です」とアプリケーション側に定義しておく必要があります。

プラグインの定義

Struts-Validatorを有効にするには、Struts-ValidatorのプラグインをStruts設定ファイルに定義する必要があります。プラグインの定義は「リスト:struts-config.xml」のように行います。プラグイン指定時に、プラグインのpathnamesプロパティとして前述の検証定義ファイルを指定します。「リスト:struts-config.xml」の 1 でvalue属性に指定しているxmlは標準検証ルールが定義されているファイルで、Strutsのjarファイルに内包されているものです。もう一方のvalidation.xmlは先に定義した検証定義ファイルです。

リスト:struts-config.xml
リスト:struts-config.xml

以上でStruts-Validatorを利用した入力検証の準備は完了です。あとは、アプリケーションを実行すれば入力検証が行われるはずです。

クライアントサイド検証の方法

Struts-Validatorには、JavaScriptによるクライアントサイド検証の機能があると前述しました。この機能を利用するのは非常に簡単です。サーバーサイドでのStruts-Validatorの検証と同様の設定を行った後、クライアント検証をしたいWebページのJSPに少し追加記述するだけです。具体的には、検証用のJavaScriptコードを出力させる場所に <html:javascript>タグを挿入し、検証を行う入力フォームを生成している<html:form>タグのonsubmit属性に検証のための関数を呼び出すように記述するだけです (「リスト:index.jsp」)。<html:javascript>タグのformName属性には、入力フォームに対応するアクションフォームBean、すなわち検証対象のアクションフォームBean名を指定します。submitボタンが押された時に検証ロジックであるJavaScript関数を呼び出す指定は、<html:form>タグのonsubmit属性で行います。onsubmit属性には “return validate+アクションフォームBean名(this);" と定義し、JavaScript関数を呼び出すように記述します (フォーム名の最初の1文字は大文字となります)。関数の本体は<html:javascript>タグによって自動的に生成されるため、前述の命名規則に従う必要があります。これだけの記述で、以下のスクリーンショットのようなクライアントサイドでの検証が可能となります。

リスト:index.jsp
リスト:index.jsp
図:クライアントサイドでの検証
図:クライアントサイドでの検証

validation.xmlの書き方

アクションフォームBeanの各属性に対する検証定義の記述方法をもう少し詳しく説明します。検証の定義は validation.xml にて行います (ファイル名は任意)。この検証定義ファイルは複数用意することも可能で、複数定義した場合にはValidatorPlugIn への指定時に、カンマ","で繋げて指定します。検証定義ファイルのフォーマットは検証ルールファイル (validator-rules.xml) と同様、commons-validatorのDTDで規定されたXMLフォーマットです。その記述は「リスト:validation.xml_2」のようになります。formsetタグのネスト内に、検証対象のアクションフォームBeanごとに form タグを定義し、そのname属性にアクションフォームBeanの名前 (Struts設定ファイルにて定義している名前) を指定します。個々の属性に対する検証の定義は field タグを用いて行います。fieldタグの property属性には属性の名前を、dependsには検証を行う検証ルール名を指定します。複数の検証ルールを適用する場合には、カンマ","をセパレータにして羅列します。この場合は左に記述したルールから評価されていきます。よって、「リスト:validator.xml_2」の例では、アクションフォームBean memberFormのmemEmail属性は、必須チェックルール、メールフォーマットチェックが適用されることになります (「リスト:validator.xml_2」の 1 )。また、エラーメッセージ用の定義として position属性が指定されており、検証ルールのデフォルトエラーメッセージの{0}プレースフォルダにはメッセージリソースキーmemberForm.memNameで定義されている文字列が挿入されるよう指定されています (「リスト:validator.xml_2」の 2 )。属性memAgeの検証定義には、検証ルールが持つデフォルトのエラーメッセージではないメッセージを検証失敗時に表示するよう、msgタグにて定義しています。その際にnameタグにて検証ルール名を指定することで、複数の検証ルールを指定していた場合にも指定のルールが失敗した場合にのみ影響するよう定義することが可能です (「リスト:validator.xml_2」の 3 )。また、多くの標準検証ルールは、その動作のためにパラメーターを必要とします。例えば、mask標準ルールには、入力値の検証のためのマスクパターンをパラメーターとして指定する必要があります。このパラメーターの与え方はvarタグを用いて行います (「リスト:validator.xml_2」の 4 )。標準検証ルールに与えるパラメーターとその意味を表にまとめたので参考にしてください。

このようにすべての検証に関する定義がXMLファイルに集約されるため、通常分散されてしまう入力検証のコードが集約化され、メンテナンス性が向上します。また、検証ルールの定義と属性への検証定義が明確に分離されるため、検証ルールを実装したコードの再利用性が向上するというメリットもあります。

リスト:validation.xml_2
リスト:validation.xml_2

標準検証ルールに与えるパラメーターとその意味

検証ルール名パラメーター指定値
required
requirediffield[数値]依存するフィールド名
fieldTest[数値]依存するフィールドに対する条件
(NULL | NOTNULL | EQUAL)
fieldValue[数値]依存するフィールドの比較値
(fieldTestがEQUALの場合のみ指定する)
fieldIndexed[数値]依存するフィールドがindexed形式か
どうかを指定する (true | false[default])
fieldJoin依存するフィールドが複数ある場合、フィールド間の依存条件
(AND[default] | OR )
validwhentest((otherField == null ) or (*this* != null))のような式
minlengthminlength最小値
maxlengthmaxlength最大値
maskmaskPerl5互換の正規表現パターン
bytebyte値の文字列
shortshort値の文字列
integerinteger値の文字列
longlong値の文字列
floatfloat値の文字列
doubledouble値の文字列
byteLocalebyte値の文字列
shortLocaleshort値の文字列
integerLocaleinteger値の文字列
longLocallong値の文字列
floatLocalefloat値の文字列
doubleLocaledouble値の文字列
datedatePattern (任意)日付パターン文字列。
フォーマットはSimpleDateFormatのコンストラクタを参照。
datePatternStrictとは排他指定
datePatternStrict (任意)厳密なチェックが行われる以外はdatePatternと同様。
datePatternとは排他指定
intRangemin最小値のinteger文字列
max最大値のinteger文字列
longRangemin最小値のlong文字列
max最大値のlong文字列
floatRangemin最小値のfloat文字列
max最大値のfloat文字列
doubleRangemin最小値のdouble文字列
max最大値のdouble文字列
creditCard
email
urlallowallschemes (任意)xxx://192.168.1.1/abc/のような未知のスキーマを許可
(true | false[default])
allow2slashes (任意)http://1.1.1.1//a/のような2重スラッシュを許可
(true | false[default])
nofragments (任意)http://1.1.1.1/a/b.html#c のようなフラグメント識別子を禁止
(true | false[default])
schemes (任意)カンマセパレータのスキーマのリスト
(default=http,https,ftp)
notEqual同名のタグと同等の機能を果たす
notMatch同名のタグと同等の機能を果たす
notPresent同名のタグと同等の機能を果たす
present同名のタグと同等の機能を果たす

独自検証ルールの作成

簡単に導入できるうえに検証ルールの集約化や再利用性の向上など強力な機能を提供してくれるStruts-Validatorですが、標準で用意されているルールのみだと我々日本人は少々物足りなさを感じます。なぜなら、我々が作るWebアプリケーションのHTML入力フィールドには、「全角カタカナのみ可」や「半角カタカナ不可」などの日本語に関する入力検証が必要不可欠なのに、これに対応する検証ルールがまったく用意されていないからです。そこで、独自の検証ルールを追加する方法を紹介します。まずは、検証のアルゴリズムをJavaのクラスとして実装する必要があります (「リスト:MyFieldChecks.java」)。このクラスにvalidateNoHalfSizeKatakanaというメソッドを用意し、半角カタカナが含まれない場合にはtrueを返却します。

リスト:MyFieldChecks.java
リスト:MyFieldChecks.java

次に、この検証用のメソッドに検証ルール名を定義します。ルール名の定義は標準検証ルールと同様にXMLファイルにて定義します。標準で用意されているルールと区別するために、別のファイルを用意するとよいでしょう。例では my-validator-rules.xmlというファイルを用意しました。検証ルールは、validatorタグのname属性に指定します (例では nothalfsizekatakana)。この検証ルールを実装しているクラスとメソッドをそれぞれ、classname属性、method属性に指定し、そのメソッドの引数の型を methodParamsにカンマ","をセパレータにして指定します。また、この検証ルールのデフォルトであるエラーメッセージのメッセージキー名をmsg属性で指定しておきます。

リスト:my-validator-rules.xml
リスト:my-validator-rules.xml

これで独自の検証ルールの定義は完了です。あとは新しいルールの定義ファイルであるmy-validator-rules.xmlを、Struts設定ファイルのStruts-Validatorのプラグイン定義に「リスト:struts-config.xml」のように追加すれば、標準で用意されている検証ルールと同様にこの新しい検証ルールを利用することが可能となります。

リスト:struts-config.xml
リスト:struts-config.xml

以上、Struts-Validatorの利用方法を簡単に紹介しましたがいかがでしたでしょうか。Struts-Validator自体は、コアとなる機能をJakarta commons-validatorプロダクトとして開発されるようになり、Struts以外からも利用が可能です。興味のある方はぜひ利用してください。

次回の記事はこちら。

  • 株式会社アークシステムの予約・来訪管理システム BRoomHubs
  • 低コスト・短納期で提供するまるごとおまかせZabbix