2007 年 4 月 16 日 23 時 14 分

キャンセル処理


このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。


日本語の問題が解決したので、次のステップに移る。
今日は、キャンセル処理について。

/WEB-INF/pages/filter/edit-info.jsp では、
タグライブラリの <html:cancel> を使って、
キャンセル用のボタンを配置しているが、
これを押すと、InvalidCancelException 例外が発生する。
まず、これを直さなければならない。

何故 <html:cancel> を配置するだけで例外が発生するのか。

<html:cancel> には「キャンセル」という特別な役割がある。
つまり、このボタンで送信した場合、
何も処理を行わないずに終了するという目的がある。

そのため、Struts はこのボタンを特別扱いしており、
これが押された場合は「フォームの検証を行わない」。
キャンセルするということは、編集内容を放棄する訳だから、
送信される内容を検証する必要がないというのが理由である。

しかし、<html:cancel> が押された場合でも、
「通常と同じように、Action が呼び出される。」
そのため、Action でキャンセルが押されたことを検出し、
if 等で分岐して適切な処理を行う必要がある。

もしこれを忘れると、<html:cancel> が押されても
通常の送信と同じように Action で処理されてしまうため、
検証を回避できてしまうというセキュリティ上の問題がある。

そのため、<html:cancel> は既定では許されておらず、
使うと InvalidCancelException 例外が発生するのだ。

さて、<html:cancel> を有効にする場合は、
キャンセルを行う事を Struts の設定ファイルに明示し、
Action でキャンセルかどうかを判別するコードを追加する。

最初に、/WEB-INF/struts-config.xml の、
/filter/update-info に対応する <action> を書き換える。

    <!-- 情報更新 -->

    <action path="/filter/update-info"
            name="filterInfoForm" scope="request"
            type="jp.loafer.test.actions.UpdateFilterInfoAction"
            cancellable="true">

        <!-- 成功時は一覧画面へ戻る -->
        <forward name="success"
                path="/filter/list.do" />

        <!-- キャンセル時も一覧画面へ戻る -->
        <forward name="cancel"
                path="/filter/list.do" />

    </action>

ポイントは、「cancellable」属性だ。
この属性値を true と明示することで、
安全装置である InvalidCancelException の発生を抑制する。

name="cancel" の <forward> は必須ではないが、
キャンセルを押した際の遷移先を指定するために追加した。

もし、cancellable="true" と指定した場合は、
必ず Action のコードを書き換えて、
キャンセルに対する処理を追加する必要がある。

========== UpdateFilterInfoAction.java ==========
package jp.loafer.test.actions;

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

import jp.loafer.test.beans.Filter;
import jp.loafer.test.forms.FilterInfoForm;
import jp.loafer.test.util.FilterManager;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

public class UpdateFilterInfoAction extends
        Action {

    public UpdateFilterInfoAction() {
        super();
    }

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

        // キャンセルを押した場合は更新しない
        if (isCancelled(request)) {
            return mapping
                    .findForward(Forwards.CANCEL_KEY);
        }

        // フォームを取得
        FilterInfoForm infoForm = (FilterInfoForm)form;

        // 現在のフィルタ設定を読み込み
        Filter filter = FilterManager.load();

        // フィルタ情報のみ更新し
        filter.setInfo(infoForm.getInfo());

        // 保存する
        FilterManager.save(filter);
       
        // OK
        return mapping
                .findForward(Forwards.SUCCESS_KEY);
    }

}
========== end of UpdateFilterInfoAction#execute ==========

<html:cancel> が押されたことは、
Action#isCancelled() によって検出することができる。

今回は、特にキャンセルに対応して行うことはないため、
「cancel」で登録した ActionForward へと転送するだけで、
もちろん、通常ならば行う更新処理は実行しない。

なお、Forwards.CANCEL_KEY は以下のような定義で、
Forwards クラスに追加してある。

    static public final String CANCEL_KEY = "cancel";

・Forwards クラスはこちら
http://mixi.jp/view_diary.pl?id=374835977&owner_id=2300658

キャンセル時でも Action は呼ばれるため、
isCancelled() を呼び出して分岐させることで、
適切な後処理やリソースの解放等を行う機会が得られるのだ。

このように、キャンセルは特別な機能として扱われている。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.