S-JIS[2009-10-12] 変更履歴

Strutsエラー処理

Strutsには、エラーメッセージをJSPで表示する機能が備わっている。
Actionクラス内で明示的にエラーをセットして画面遷移させる方法と、発生した例外を処理する方法がある。


エラーメッセージ表示

普通に画面(JSP)にエラーメッセージを出力する場合の処理の流れは、

  1. Action#execute()エラーメッセージを生成する
  2. JSPでエラーメッセージを出したい箇所にhtml:errorsタグを使う

となる。
生成する際、メッセージ自体はプロパティーファイル(メッセージリソースファイル)に書いておき、プログラムからはそのキーを指定することになる。
html:errors(ErrorsTagクラス)がキーから文言に変換する。


メッセージリソースファイルの準備

Strutsのサンプルによると、メッセージの文言を書いておくファイルはMessageResources.propertiesらしい。
中身は普通のプロパティーファイルと同様の書き方。

#comment
errors.msg.key1=HogeHoge
errors.msg.key2=zzz {0},{1}

このファイルの名前および場所はstruts-config.xmlで指定する。

<struts-config>
〜
	<message-resources parameter="MessageResources" />

</struts-config>

message-resources要素のparameter属性に、ファイル名の拡張子を除いた部分を書く。
「MessageResources」と指定した場合、実行環境が日本であれば、
MessageResources_ja_JP.properties
MessageResources_ja.properties
MessageResources.properties
のうち、存在している上位のファイルが使われる。(たぶん、ResourceBundleの仕組みが使われている)

実際にこれらのファイルを置く場所は、コンパイルされたclassファイルと同じ場所。
Eclipseの場合、src(javaソースファイルの置き場所)と同じところに置けば、自動的にclassファイルの場所にコピーしてくれる。
上記の例だと、パッケージ(ディレクトリー)を何も指定していないので、デフォルトパッケージ扱い、すなわちsrcの直下にファイルを置くことになる。
src/jp/hishidama/message/MessageResources.propertiesを使いたいなら、
<message-resources parameter="jp/hishidama/message/MessageResources" />」あるいは
<message-resources parameter="jp.hishidama.message.MessageResources" />」となる。


エラーメッセージの生成

Action#execute()の中でエラーをセットする方法。

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionMessages;
public class Sample4Action extends Action {

	@Override
	public ActionForward execute(ActionMapping mapping, ActionForm form,
		HttpServletRequest request, HttpServletResponse response) throws Exception {

		ActionErrors errors = new ActionErrors();

		ActionMessage msg = new ActionMessage("errors.msg.key1");
		errors.add(ActionErrors.GLOBAL_MESSAGE, msg);

		addErrors(request, errors);	//親クラスのメソッドを呼び出す

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

Actionクラス内に、addErros()という、エラーメッセージを保持するメソッドがあるので、これを呼び出す。
この引数はActionErrorsクラスであり、複数のエラーメッセージを保持できる。
なので、名前を付けてadd()する。代表的なエラーを保持する場合は、GLOBAL_MESSAGEという名前を使うらしい。
(ActionErrorsはActionMessagesのサブクラスであり、GLOBAL_MESSAGEもActionMessagesで定義されている)

ActionMessageのコンストラクターの第1引数にはメッセージキーを指定する。
実際の文言は、JSPに出力される際にメッセージリソースファイルから取得される。
その文言の中に可変項目({0}や{1}など)がある場合、ActionMessageのコンストラクターの第2引数以降でその値を指定する。
可変項目が2つあったとすると、以下のようになる。

		ActionMessage msg = new ActionMessage("errors.msg.key2", "可変値0", "可変値1");
あるいは
		ActionMessage msg = new ActionMessage("errors.msg.key2", new Object[]{ "可変値0", "可変値1" });

Action#addErrors()の中でやっていることは、主にリクエストアトリビュートにActionErrorsを保持すること。
(既に保持していた場合は、そこに追加する)

保持する際のキー名はGlobals.ERROR_KEY(値は"org.apache.struts.action.ERROR")が使われる。


JSPファイルでのエラーメッセージ表示

JSPファイルでは、html:errorsタグを使ってエラーメッセージを表示する。

<%@ page contentType="text/html; charset=Windows-31J"%>
<%@taglib uri="/struts-html" prefix="html"%>
<html:html>
<head>
<title>error sample</title>
</head>
<body>
<h1>エラーサンプル</h1>
<h2>Strutsのhtml:errors</h2>
<p><html:errors /></p>
</body>
</html:html>

html:errorsに属性を何も指定しない場合、Actionで登録した複数のエラーメッセージが全て表示される。
エラーが何も登録されていない場合は、何も表示されない。

property属性にActionErrorsでメッセージを追加した際の名前を指定すると、そのメッセージだけが表示される。

<p><html:errors property="org.apache.struts.action.GLOBAL_MESSAGE" /></p>

name属性を指定すると、リクエストアトリビュートからその名前で登録されたActionErrors(厳密にはActionMessages)が使われる。
省略時はGlobals.ERROR_KEYが使われる。(すなわち、addErrors()のデフォルト状態)


html:errorsの出力例

Actionクラスでのエラーメッセージの生成 JSPでの出力方法 出力例 備考
errors.add(ActionErrors.GLOBAL_MESSAGE, msg0);
addErrors(request, errors);
<html:errors /> 共通エラー  
errors.add("err1", msg1);
addErrors(request, errors);
<html:errors /> エラー1  
<html:errors property="err1"/> エラー1 プロパティー属性指定時
errors.add(ActionErrors.GLOBAL_MESSAGE, msg0);
errors.add("err1", msg1);
addErrors(request, errors);
<html:errors /> 共通エラーエラー1 メッセージの文言に改行「<br>」等が含まれていない場合、
複数のメッセージがくっついて出てしまう。
<html:errors property="err1"/> エラー1  
同上 <html:errors
 header="head.key"
 footer="foot.key"
 prefix="pre.key"
 suffix="suf.key"
/>
<p><font color="red">
**pre**共通エラー**suf**<br>
**pre**エラー1**suf**<br>
</font></p>
メッセージリソースファイルにヘッダー・フッター類を指定した例
head.key=<p><font color="red">
foot.key=</font></p>
pre.key=**pre**
suf.key=**suf**<br>
同上 <html:errors /> <p><font color="red">
**pre**共通エラー**suf**<br>
**pre**エラー1**suf**<br>
</font></p>
ヘッダー・フッター類にはデフォルトのキーがあるので
メッセージリソースファイルにそれを設定しておけば
JSP上のerrorsタグの属性は省略可能
errors.header=<p><font color="red">
errors.footer=</font></p>
errors.prefix=**pre**
errors.suffix=**suf**<br>
errors.add(ActionErrors.GLOBAL_MESSAGE, msg2);
request.setAttribute("MYERR", errors);
<html:errors name="MYERR" /> 共通エラー2 name属性指定時

例外処理

Action#excute()の中で例外が発生した際に、その例外をキャッチすることが出来る。

struts-config.xmlのアクションマッピング(action-mappingsのaction要素)内に記述した場合はそのアクションのみ、
全アクション共通でキャッチしたい場合はglobal-exceptionsを記述する。


actionのexception

アクション固有の例外捕捉を行いたい場合、action-mappingsの該当action要素内にexception要素を記述する。

	<action-mappings>
〜
		<action path="/sample4submit" type="jp.hishidama.sample.struts.sample4.Sample4Action" name="sample4Form" scope="request">

			<exception type="jp.hishidama.sample.struts.sample4.Sample4Exception"
				path="/error.jsp"
				key="msg.key.exception" />

			<forward name="success" path="/index.jsp" />
			<forward name="failure" path="/error.jsp" />
		</action>

	</action-mappings>

exception要素のtype属性に、キャッチしたい例外クラス名を書く。
その例外の全ての種類のサブクラスが捕捉対象になる。
(別途サブクラスをtype属性に記述したexception要素があれば、そちらが優先される。struts-config.xml内の記述順序は無関係)

path属性は、その例外をキャッチした際に遷移する画面(パス・URI)を指定する。
JSPファイルだけでなく、他のアクションへ遷移(フォワード)することも出来る。
省略した場合は、action要素のinput属性のパスに遷移するらしい。

key属性は、エラーメッセージの文言を表すキー。(DTDを見ると必須項目ではないのだが、必須っぽい)
メッセージリソースファイルからそのキーで文言が取得される。
このキーでActionErrorsが作られてリクエストにGlobals.ERROR_KEYで保持されるので、遷移先のエラー画面 ではhtml:errorsを使ってその文言を表示できる。
(例外を発生させた箇所に応じてメッセージを指定したい場合は、ModuleExceptionを使う。)

アクションクラスで例外を出力する例:

	@Override
	public ActionForward execute(ActionMapping mapping, ActionForm form,
		HttpServletRequest request, HttpServletResponse response) throws Exception {

		throw new Sample4Exception();
	}

メッセージリソースファイル

msg.key.exception=struts-config.xmlで指定されたメッセージ

ModuleException

例外をキャッチして遷移した先のJSPのhtml:errorsタグでは、exception要素のkey属性で指定されたメッセージを表示できる。
ただしこれでは、例外発生箇所ごとに別の文言を指定することが出来ない。(struts-config.xml上で固定のキーだから)

ModuleExceptionという例外を使えば、例外発生箇所でエラーメッセージを指定することが出来る。

アクションクラス:

import org.apache.struts.util.ModuleException;
	@Override
	public ActionForward execute(ActionMapping mapping, ActionForm form,
		HttpServletRequest request, HttpServletResponse response) throws Exception {

		throw new ModuleException("msg.key.module-exception");
//		throw new ModuleException("msg.key.module-exception2", "引数0", "引数1");
//		throw new ModuleException("msg.key.module-exception2", new Object[]{ "引数0", "引数1" });
		//コンストラクターの引数の種類は、ActionMessageと同じ
	}

メッセージリソースファイル

msg.key.exception=struts-config.xmlで指定されたメッセージ
msg.key.module-exception =明示的に指定したメッセージ
msg.key.module-exception2=明示的に指定したメッセージ{0}{1}

struts-config.xml:

			<exception type="org.apache.struts.util.ModuleException"
				path="/error.jsp"
				key="msg.key.exception" />

例外ハンドラー

例外をキャッチした際に、独自の処理を行うことも出来る。
struts-config.xmlのexception要素にhandler属性を指定する。

	<action-mappings>
〜
		<action path="/sample4submit" type="jp.hishidama.sample.struts.sample4.Sample4Action" name="sample4Form" scope="request">

			<exception type="jp.hishidama.sample.struts.sample4.Sample4Exception"
				path="/error.jsp"
				key="msg.key.exception"
				handler="jp.hishidama.sample.struts.sample4.Sample4ExceptionHandler" />

			<forward name="success" path="/index.jsp" />
			<forward name="failure" path="/error.jsp" />
		</action>

	</action-mappings>

Sample4ExceptionHandler.java:

package jp.hishidama.sample.struts.sample4;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.Globals;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ExceptionHandler;
import org.apache.struts.config.ExceptionConfig;

public class Sample4ExceptionHandler extends ExceptionHandler {

	@Override
	public ActionForward execute(Exception ex, ExceptionConfig ae,
		ActionMapping mapping, ActionForm formInstance,
		HttpServletRequest request, HttpServletResponse response) throws ServletException {

		System.out.println(ex.getClass());

		//メッセージ生成
		ActionMessage msg = new ActionMessage("msg.key.handler");

		String property = Globals.ERROR_KEY;
		ActionForward forward = new ActionForward(ae.getPath());
		String scope = "request";

		//リクエストにエラーメッセージを保存
		storeException(request, property, msg, forward, scope);

		if (!response.isCommitted()) {
			return forward;
		}
		return null;
	}
}

例外ハンドラーは、ExceptionHandlerを継承し、execute()をオーバーライドする。
第1引数は捕捉した例外。
第3引数のmappingは(Action#execute()のmappingと同様に)struts-config.xmlのaction要素の内容を保持している。
同様に、第2引数のExceptionConfigはexception要素の内容を保持している。

例外ハンドラーの中でActionMessageを生成してリクエストに詰めてやれば、遷移先からhtml:errorsでメッセージを表示することが出来る。
これにはstoreException()というメソッドが使える。(ActionのaddErrors()と似たようなもの)

あるいは、遷移先(forward)もここで変更することが出来る。


struts-config.xmlのexception要素でhandler属性を省略した場合、ExceptionHandlerクラスそのものが使われるようだ。

ModuleExceptionに指定したメッセージ情報からActionMessageやActionErrorsを作り出してリクエストにセットするのは、ここで行われている。


global-exceptions

struts-config.xmlの個別のaction要素に捕捉する例外を書くのではなく、全てのアクションからの例外を共通にキャッチしたい場合は、global-exceptionsを使う。

<struts-config>

	<form-beans>
〜
	</form-beans>

	<global-exceptions>
		<exception type="jp.hishidama.sample.struts.sample4.Global4Exception"
			path="/error.jsp"
			key="msg.key.exception"
			handler="jp.hishidama.sample.struts.sample4.Sample4ExceptionHandler" />
	</global-exceptions>

	<action-mappings>
〜
	</action-mappings>

</struts-config>

global-exceptionsの中にexcpetion要素を書く。書く内容はaction要素内に書く場合と同じ。


web.xmlにはerror-pageという要素があって、そこでも例外をキャッチして処理することが出来る。

global-exceptionsはそのstruts-config.xmlの属するモジュールの範囲でしか例外をキャッチできない(キャッチしない)が、
web.xmlのerror-pageの方はそこで漏れた例外も全て対象となる。
(Strutsと無関係に処理するようなもの(例えばフィルター)での例外は、error-pageでしかキャッチできない)

Strutsのhtml:errorsタグを利用したエラーページへ遷移させる場合、
error-pageでキャッチした方はStruts用のデータが入っているとは限らない為、注意が必要となる。


Actionへ戻る / Struts目次へ戻る
メールの送信先:ひしだま