昨日のようにリクエスト属性を使い、
メッセージを JSP に渡す方法でも構わないのだが、
これでは規模が大きくなってくると管理が難しくなる。

Struts には、フォームの検証を行い、
その検証結果を元にメッセージを作成し
それを JSP に引き渡す仕組みを持っている。

今度は、Struts の持つ検証機能を使って、
入力の検証やメッセージ表示を行ってみよう。

まずは、検証の部分からやっていこう。

ActionForm には、自身の内容を検証するための、
validate メソッドがある。

    ActionErrors validate(
            ActionMapping mapping,
            HttpServletRequest request);

昨日まででは、Model に検証コードを書いていたが、
このメソッドに検証コードを写すことで、
Model を本来の処理だけに集中させることができる。

=========== LoginForm#validate ===========

    public ActionErrors validate(ActionMapping mapping,
            HttpServletRequest request) {

        ActionErrors errors = new ActionErrors();

        if (user == null || user.length() == 0) {
            ActionMessage m =
                    new ActionMessage(
                            "ユーザ名を入力してください。", false);
            errors.add("user", m);
        }
        if (password == null || password.length() == 0) {
            ActionMessage m =
                    new ActionMessage(
                            "パスワードを入力してください。", false);
            errors.add("password", m);
        }

        if (!errors.isEmpty()) return errors;

        // ユーザ名は guest 限定とする
        // パスワードは hogehoge 限定とする

        if (!"guest".equals(user)
                || !"hogehoge".equals(password)) {
            ActionMessage m =
                    new ActionMessage(
                            "ユーザ名またはパスワードが違います。",
                            false);
            errors.add(null, m);
        }

        return errors;

    }

=========== end of LoginForm#validate ===========

validate では、フォーム自身で内容の検証を行う。
戻り値は、ActionErrors クラス型である。

ActionErrors は検証で発生したエラーを保持するクラスで、
ある 1 つのエラーを示す ActionMessage インスタンスを、
複数同時に格納することができる。

フォームの内容に問題がなかった場合は、
null または ActionMessage を 1 つも持たない、
空の ActionErrors インスタンスを返せばよい。

上記処理では、昨日よりも 1 歩進め、
ユーザ名やパスワードが空の場合もエラーとしてみた。

エラーメッセージは、ActionMessage インスタンスで表す。
2 番目の引数に false を指定すると、
メッセージを直接格納することができる。

本来 ActionMessage では、直接メッセージを指定せず、
ResourceBundle から文字列を取得するのが好ましいのだが、
まずは使い方ということで、直接指定してみた。

さて、ActionMessage を作成したら、
ActionErrors の add メソッドを呼び出して、
ActionErrors にメッセージを追加するのだが、
add メソッドは、ActionMessage の他に、
もう 1 つ文字列の property 引数を取る。

    void add(String property, ActionMessage message);

ActionErrors の内部では、ActionMessage を
幾つかのグループに分類することができる。
add メソッドの第 1 引数は、グループの名前だ。

一般的には、エラー(ActionMessage)を、
それが発生した原因であるプロパティに関連付けるため、
グループ名として、プロパティ名を指定する。

例えば、ユーザ名の入力を促すエラーは、
そのプロパティ名である「user」グループへ、
パスワードの入力を促すエラーは、
そのプロパティ名である「password」グループへ、
そしれ、認証の失敗を示すエラーは、
フィールドを特定できないので、null のグループとした。

これで、検証用のコードはフォームに移動したため、
LoginAction の方では検証コードを消すことができる。

=========== LoginAction#execute ===========

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

        // パラメータを取得
        LoginForm f = (LoginForm)form;

        // ログイン成功!

        // 「次回から入力を省略」にチェックがあれば
        if (f.isPersistent()) {
            // TODO: Cookie 等に情報を保存
        }

        // ログイン情報をセッションに保存
        request.getSession().setAttribute("user", f.getUser());

        return mapping.findForward(Forwards.SUCCESS_KEY);

    }

=========== end of LoginAction#execute ===========

これで Model から検証処理を切り離すことができた。
少し保守性が増したと考えることができる。