Struts1 [Validator]

Validator はアクションフォームの検証を外部で行うプラグイン機能です。コレによりDynamicActionForm の メリットを享受しつつ、検証機能が有効になります。DynaValidatorForm クラスを使う事になります。前回のサンプル(bbs01.war) をベースに、実装してゆきます。bbs02 として使います。今回のサンプル(bbs02.war)
更新日 2016-02-13

DynaValidatorForm に置き換える

まずDynaVidatorForm をアクションフォームとします。次に

設定の編集

/WEB-INF/struts-config.xml にDynaValidatorForm のインスタンスを設定します。
<form-beans>

	<!-- 追加 -->
	<form-bean name="bbs_dvf"
				type="org.apache.struts.validator.DynaValidatorForm">
		<form-property name="str" type="java.lang.String" />
	</form-bean>
</form-beans>
次にこのアクションフォームを使うようaction エレメントの設定を修正します。
<action path="/bbs"
                type="mypackage.bbsAction"
                name="bbs_dvf"
                scope="request"
                validate="true"
				input="/error.jsp">
	<forward name="success" path="/bbs.jsp"/>
	<forward name="error" path="/error.html"/>
</action>
validate 属性をtrueにし、input 属性にはエラー時にフォワードする先のJSP を設定します。
最後にValidator を機能させる為に、プラグインとして設定しておきます。
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
	<set-property property="pathnames"
	 value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
</plug-in>

アクションクラスの修正

フォームにアクセスする際のキャストをDynaActionForm からDynaValidatorForm に変更する。 DynaValidatorForm のimport も忘れずに。文字列がゼロの時の処理はDynaValidatorForm に任せる 為、除去します。
// Compile command. You must be current file's directry.
// javac -classpath "../lib/struts.jar;../../../../common/lib/servlet-api.jar;"
 -d "." bbsAction.java

package mypackage;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForward;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.DynaActionForm;
import org.apache.struts.validator.DynaValidatorForm;     // 追加

public class bbsAction extends Action
{
	public ActionForward execute
	(
		ActionMapping mapping,
		ActionForm form,
		HttpServletRequest req,
		HttpServletResponse res
	)
	throws Exception
	{
		// アクションフォームにアクセスできます。
		DynaValidatorForm myForm = (DynaValidatorForm)form;   // 修正

		return mapping.findForward( "success" );
	}
}

validation.xml を/WEB-INF に新規作成

Validator プラグインが読み込む、検証の為の設定ファイルを自作します。
<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE form-validation PUBLIC
 "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.1.3//EN"
 "http://jakarta.apache.org/commons/dtds/validator_1_1_3.dtd">

<form-validation>
    <formset>
        <form name="bbs_dvf">
            <field property="str" depends="required">
            </field>
        </form>
    </formset>
</form-validation>

エラー時のページ(error.jsp) を作成

action の設定でinput 属性に設定した/error.jsp ファイルを作成します。
<%@page contentType="text/html; charset=Shift_JIS" pageEncoding="Shift_JIS" %>
<%@ taglib uri="/tags/struts-html" prefix="html" %>
<%@ taglib uri="/tags/struts-bean" prefix="bean" %>

<html:html locale="true">
	<head>
		<title>error.jsp</title>
	</head>
	<body>
		<h3>Error!!</h3>
		<div>
			<html:errors />
		</div>
	</body>
</html:html>

bbs.jsp の修正

bean:write のname 属性をbbs_dvf(DynaValidatorForm インスタンス名)に修正します。
ブラウザで表示して、bbs01 の時と変わらない事をご確認ください。テキストボックス見入力で 実行を押すと、error.jsp が表示されます。{0} is required. と表示されたら成功です。

エラーメッセージを変更する。

デフォルトのエラーメッセージの変更(errors.required 等)、それに対する一部上書き({0} とarg0)、 そして対象プロパティ毎に独自のエラーメッセージの割り当ての三つの方法があります。 前者の二つの組み合わせだけで相当をカバーできますが、それに囚われない方法も可能という事です。

下準備

エラー時のメッセージはメッセージリソースというテキストファイルに記述されています。struts-config.xml の message-resources エレメントのparameter 属性がソレです。デフォルトではMessageResources となっており、 該当するファイル(MessageResources.properties) が/WEB-INF/classes に存在しています。
struts-config.xml を修正します。今回はbbs_MR_ja.properties というファイルを新たに用意して参照させます。
<!-- ======================= Message Resources Definitions -->
<!-- parameter 値を変更 -->
<message-resources parameter="bbs_MR"/>
.properties はもちろん、ロケール識別子(_ja) も省略されている事に注意して下さい。これらは言語毎にファイルを 別に準備できる事を示しています。ファイルは/WEB-INF/classes 以下に配置でき、ロケーションパスが深くなる場合は Java のパッケージのようにドットで区切って表記します。(例:/WEB-INF/classes/filter/bbs_MR_ja.properties なら filert.bbs_MR )
なおロケール識別子は_ja です。_jp では動きません!間違えやすいので注意!

メッセージリソースとは?

言語ロケール識別子 + .properties という形で終わるファイルで、Java で管理されるリソースです。Web アプリでは クライアントの希望するロケールでリソースを切り替える事で多言語化を可能にしています。メッセージリソースは JSP から簡単に参照でき、html:message タグで取り出せます。一つのJSP ファイルで複数の言語ロケールに応じる事が できる訳です。

デフォルトのエラーメッセージの変更

次に今回はbbs_MR_ja.properties でした。まず下書きとしてbbs_MR というファイル名で作成して以下の内容を書きます。
errors.required={0} は入力必須項目です。
Java ではマルチバイト文字列はUnicode で扱っているので、そのバイトコードをプレフィックス\ を付けて ワード毎に書き直します。その為のコマンドがnative2ascii でJavaSDK に入ってます。以下のコマンドで実行します。
native2ascii bbs_MR bbs_MR_ja.properties
bbs_MR_ja.properties が以下の内容で作成されてる事を確認して下さい。
errors.required={0} \u306f\u5165\u529b\u5fc5\u9808\u9805\u76ee\u3067\u3059\u3002
Tomcat を再起動します。何も入力せずに実行ボタンを押すとerror.jsp に遷移し、<html:errors /> によって 「{0} は入力必須項目です。」と出力されます。

メッセージ中の{0} に文字を割り当てる。

メッセージリソース中の{0} には、validation.xml で設定しておいた文字列を実行時に割り当てる事ができます。 arg0, arg1, arg2 の三つのエレメントをfield エレメントに配置でき、それぞれが{0}, {1}, {2} に対応します。
<form-validation>
    <formset>
        <form name="bbs_dvf">
            <field property="str" depends="required">
				<!-- 追加 -->
				<arg position="0" key="text-box" resource="false" />
            </field>
        </form>
    </formset>
</form-validation>
これで実行時には「text-box は入力必須です。」との出力になります。key に文字列を直接書いているので、resource 属性を false としています。そしてメッセージリソースファイルにmytextbox.text=名前 と準備してあるなら、arg のkey 属性に "mytextbox.text" として、resource 属性を除けばいいわけです。なお文字列を直接記述できるといっても、日本語等は扱え ません。その場合は、やはり外部メッセージリソースに記述する訳です。

注意

struts1.2 からはarg0〜arg3 は非推奨になりました。arg タグにposition 属性を用います。
さらに別途に作成したvar エレメントの値を参照するようkey 属性に指示する事もできます。
<form-validation>
    <formset>
        <form name="bbs_dvf">
            <field property="str" depends="required">
				<!-- key 属性を修正 -->
				<arg0 key="${var:min}" resource="false" />

				<!-- 追加 -->
				<var>
					<var-name>min</var-name>
					<var-value>5</var-value>
				</var>
            </field>
        </form>
    </formset>
</form-validation>
${var:min} は文字列ではなく、同じ親エレメントに属するvar エレメントのvar-name に該当するソレを参照していると見なされます。 そしてその値(var-value エレメント)を出力します。required の時は単なる二度手間で意味無いのですが、別の検証ルール においてはvar エレメントによって検証の為のしきい値が設定されたりするので、arg0 とva-vavlue にそれぞれ同じ値を設定するより var-value を参照させた方が保守性が高まる訳です。

検証対象のプロパティ毎にメッセージを変える。

errors.required は未入力チェックのデフォルトのエラーメッセージなので、変更すると同じWeb アプリ内の別のアクション の検証においても反映されてしまいます。そこでerrors.required とは違う名前でメッセージを準備しておき、 個別にそれを適用させます。msg エレメントがそれです。
<form-validation>
    <formset>
        <form name="bbs_dvf">
            <field property="str" depends="required">
				<!-- 追加 -->
				<msg name="required" key="NameTextBox.text" />

				<arg0 key="text-box" resource="false" />
            </field>
        </form>
    </formset>
</form-validation>
これでアクションフォームのプロパティstr のrequired 検証時に限って、errors.required では無くNameTexBox.text が 用いられる事になります。もちろんメッセージリソースに「NameTextBox.text={0} を入力して下さい」等の記述も忘れずに。

検証パターンいろいろ

DynaValidatorForm では以下の検証ルールが存在し、アクションフォームのプロパティそれぞれに一つまたは組み合わせて 適用させる事ができます。その指示は/WEB-INF/validation.xml に記述します。

required

値がnull もしくは空白文字の場合にエラーとなります。デフォルトのエラーメッセージはerrors.required です。
<field property="str" depends="required">
	<arg0 key="名前" resource="false" />
</field>

requiredif

required と同じ検証ルールですが、他のプロパティ値が指定の条件と合致した場合に適用されます。以下の例では、 プロパティ名check の値が1である場合に限りrequierd 検証を行っています。n は0 から始まる数です。複数定義する時は、その都度この数値をす。
<field property="str" depends="requiredif">
	<arg0 key="名前" resource="false" />
	<var>
		<var-name>field[n]</var-name>
		</var-value>check<var-value>
	</var>

	<var>
		<var-name>fieldTest[n]</var-name>
		<var-value>EQUAL</var-value>
	</var>

	<var>
		<var-name>fieldValue[n]</var-name>
		<var-value>1</var-value>
	</var>
</field>
field にはプロパティ名、fieldTest には条件(NULL, NOTNULL, EQUAL)、 fieldValue にはチェックの為の値、 fieldJoin には複数の条件時における AND もしくはOR の設定(fieldJoin 無しの場合はAND)。

minlength, maxlength

文字数をチェックします。例えば2文字以上、10 文字以下の場合。
<field property="str" depends="minlength,maxlength">
	<arg1 key="${var:minlength}" resource="false" />
	<arg2 key="${var:maxlength}" resource="false" />
	<var>
		<var-name>minlength</var-name>
		</var-value>2<var-value>
	</var>
	<var>
		<var-name>maxlength</var-name>
		</var-value>10<var-value>
	</var>
</field>
arg1 とarg2 でメッセージリソース中の{1} ならびに{2} に置き換えられる文字列を設定 しています。それも${} を用いて変数(var) の値を参照するようにしています。 それぞれ${var:minlength}(この場合は2)、${var:maxlength}(この場合は10) に置き換えられわけです。

min, max

文字列を数値に変換し、それぞれの最小値・最大値と比較検証します。例えば、-100 から100 の 範囲であるべきなら。
<field property="str" depends="min,max">
	<arg1 key="${var:min}" resource="false" />
	<arg2 key="${var:max}" resource="false" />
	<var>
		<var-name>min</var-name>
		</var-value>-100<var-value>
	</var>
	<var>
		<var-name>max</var-name>
		</var-value>100<var-value>
	</var>
</field>
byte, short, int, long, float, double といった型で範囲を指定する事もできます。

mask

正規表現を使ったマッチング検証です。例えばA で始まる文字列のときはA.* と書きます。
<field property="str" depends="mask">
	<var>
		<var-name>mask</var-name>
		</var-value>A.*<var-value>
	</var>
</field>

email

メールアドレスの形式であるかチェックします。
<field property="str" depends="email">
</field>

date

日時の形式であるかチェックします。Strict の方は文字列長も合致しないといけません。
<field property="str" depends="date">
	<var>
		<var-name>datePatternStrict</var-name>
		</var-value>yyyy-mm-dd<var-value>
	</var>
</field>

JavaScript による入力チェックを自動生成する

html:javascript タグを用いると検証ルーチンをJavaScript で生成してくれます。そして html;form のonsubmit 属性に上記のスクリプトを呼び出す記述を書けばOKです。便利ですね。

bbs.jsp の編集

<%@page contentType="text/html; charset=Shift_JIS" pageEncoding="Shift_JIS" %>
<%@ taglib uri="/tags/struts-html" prefix="html" %>
<%@ taglib uri="/tags/struts-bean" prefix="bean" %>

<html:html locale="true">
	<head>
		<title>bbs.jsp</title>
		<html:javascript formName="bbs_dvf" />	
	</head>
	<body>
		<div>
			<html:form action="/bbs" method="POST"
				 onsubmit="return validateBbs_dvf(this);">
				<html:text property="str" size="20" />
				<html:submit property="submit" value="実行" />
			</html:form>
		</div>
		<pre><bean:write name="bbs_af" property="all" scope="request" /></pre>
	</body>
</html:html>
自動生成されたスクリプトの関数名はvalidate + フォーム名(先頭は大文字) です。引数にthis を 渡します。