LookupDispatchAction + エンター
izuさんのところのむかーしの記事のタイトルをぱくってみました。
LookupDispatchActionはクリックされたボタンにより処理を振り分けるもの。
例えばこんな風にしておけば、
<html:form action="/hoge"> <html:submit property="action"><bean:message key="button.moge"/></html:submit> <html:submit property="action"><bean:message key="button.hage"/></html:submit> </html:form>
クリックされたボタンにより処理の振分が行われる。
ところが、次のようにフォーム内に入力フィールドなどのオブジェクトが一つだけある場合に困ったことになる。
例えばこんなソース。
<html:form action="/hoge"> <html:input property="hogehoge"/> <html:submit property="action"><bean:message key="button.moge"/></html:submit> <html:submit property="action"><bean:message key="button.hage"/></html:submit> </html:form>
このJSPをIEからPOSTするのだが、入力フィールドにカーソルを当てて、enterキーを叩くと画面が白くなる。
理由はStrutsのソースをデバッグしてみると分かるのだが、LookupDispatchActionのgetMethodName()内で「どのボタンがクリックされたのか」のパラメータ(keyName)が取得できていないため、nullが返却されるのだ。
protected String getMethodName( ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response, String parameter) throws Exception { // Identify the method name to be dispatched to. // dispatchMethod() will call unspecified() if name is null String keyName = request.getParameter(parameter); if (keyName == null || keyName.length() == 0) { return null; } String methodName = getLookupMapName(request, keyName, mapping); return methodName; }
当然Forward先"null"は存在しないので画面が白くなるというわけだ。
※複数のフィールドがある場合や、FireFoxではこの現象はでていない。その画面の最も最初に出てくるボタンのパラメータが送られてくる。
※requestをデバッグしてみると、「request-coyoteRequest-parameters-paramHashStringArray-table」の中に正常な場合はディスパッチのキーが含まれているのが分かる。
さてどうするか。
izuさんはデフォルトのForwardを定義しているが、今回は大先輩に逆らってみよう。
ディスパッチを使用する場合、僕はvalidateを飛ばした先で行っている。
そこで、validateで使用するinputを借用して戻る画面を定義してみることに。
<action path="/hoge" parameter="action" type="hoge.MyDispatchAction" scope="request" input="/hogeInput" validate="false"> <forward name="moge" path="/moge.do"/> <forward name="hage" path="/hage.do"/> </action>
izuさんの案に手を入れたもの
hoge.MyDispatchActionはLookupDispatchActionを継承したこのクラスをさらに継承する。
public abstract class LookupDispatchCustomAction extends LookupDispatchAction { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response ) throws Exception { if ( request.getParameter( mapping.getParameter() ) == null ) { String input = mapping.getInput(); if(input != null){ //inputが定義されている ActionForward af = new ActionForward(); af.setPath(input); af.setRedirect(false); return af; }else{ // 不明 throw new UnknownDispatchException(); } } else { //LookupDispatchActionに処理をまかせる return super.execute( mapping, form, request, response ); } }
abstractで定義してあるのは、スーパークラスのLookupDispatchActionがgetKeyMethodMap()を必要とするため。
こうしておけば、パラメータが送られてこないときでもinputが定義されていればその画面に飛ぶし、なければきちんとエラーとして扱うことができる。
もう一つ考えたのは、Actionクラス全般を処理後にインターセプトして、ActionForward先がnullの場合はエラーにするもの。
AOPを使えばできるだろうけど、後から分かり難くなりそうなので今回はちょっとパス。
参考
izuさんの日記*1
@ITに同じような質問があったけれども、解決策は提示されず*2
あとは海外の掲示板で2〜3件あったぐらいかな?
今回Strutsのソースを直接調べたのが勉強になったなぁ・・・
やっぱり気になったので後日修正
"default"というforwordを定義しておいて、存在しないならエラーにすることに
private static final String DEFAULT_FORWORD_NAME = "default"; public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response ) throws Exception { if ( request.getParameter( mapping.getParameter() ) == null ) { ActionForward def = mapping.findForward(DEFAULT_FORWORD_NAME); if(def != null){ //defaultが定義されている場合はそこに戻るようにする return def; }else{ // それ以外はエラー画面に遷移する throw new UnknownDispatchException(); } } else { //LookupDispatchActionに処理をまかせる return super.execute( mapping, form, request, response ); } }