menu
書いてる野郎
orebike@gmail.com
つまりバリデータ(Validator)とか言われる処理。
formの記述に関しての注意は SAStruts/ActionForm にもあるので参考にしてくれ。
とうぜんながらここでの説明は Action クラスの内部記述に関してしている。
入力チェックが必要なシチュエーションは大体何かの値の更新作業で POST メソッドで渡される場合が多く、 このような場合 POST-Redirect-GET パターン(PRGパターン)が用いられることが多い。
なので redirect して押し返す。redirect して任意のメソッドに押し返したいと思えば
@Execute(validator=true, input="piyo?redirect=true")
のように Action メソッドでの redirect と同じように記述すればよい。
もちろん
@Execute(validator=true, input="hoge.jsp")
と指定して、通常どおりjspを呼び出すこともできる。 これはGETなどで検索用にパラメータを受け取った場合に条件がおかしいことを伝えるような場合に使うと考えられる。
このように、戻り先に自分自身を指定した場合、エラーの場合無限ループ(再びvalidatorが評価される)する。なのでActionで前処理が必要なエラー画面を作るなら別Action メソッドを改めて作ることになる。
@Execute(validator=true, input="index") public String index(){
パラメータ付きの URL を使いたい場合は送られてきたformのプロパティ値をそのまま使うことができる。
@Execute(validator=true, input="piyo/{hoge}?redirect=true")
のように記述すれば form の hoge プロパティの値を流用して redirect することができる。 このプロパティの値は非アスキー文字はだめっぽい。
input に記述する戻し先の制御に介入する余地が狭いので、 様々な戻り先を持つ場合は共通の action メソッドを使わずに、戻し先ごとに action メソッドを用意したほうがスマートに書けるだろう。
//内部処理は同じだがinputの記述を変えたいので別メソッドを作る @Execute(validator=true, input="piyo?redirect=true") public String piyopiyo(){ return hoge(); } //内部処理は同じだがinputの記述を変えたいので別メソッドを作る @Execute(validator=true, input="fuga?redirect=true") public String fugafuga(){ return hoge(); } private String hoge(){ //何か共通の処理やる return "hoge.jsp"; }
エラーメッセージを表示するには対象の JSP に
<!-- エラーメッセージを一気に表示 --> <html:errors /> <!-- hogeプロパティに関するエラーメッセージを表示 --> <html:errors property="hoge"/>
のように書く。
通常、エラーメッセージ自体はデフォルトでrequestスコープに書き込まれる。
redirect 指定でやった場合は request が消滅している(redirect 後だから2回めの request になっている)ので redirect した先の action ではエラーメッセージを読み出すことができない。
そこでエラーメッセージのスコープを Session スコープに切り替えることができる。
action メソッドのアノテーションで
@Execute(validator=true, input="hoge?redirect=true", saveErrors=SaveType.SESSION)
のように記述する。
これでエラーメッセージがセッションスコープに格納されるのでredirect先でも取り出すことができる。
エラーメッセージ同様 form はリクエストスコープなのでリダイレクトしてしまうと揮発してしまう。
なので揮発しないようにformクラスにアノテーションをつけてセッションスコープにしてしまう
@Component(instance=InstanceType.SESSION) public class HogeForm implements Serializable{ private static final long serialVersionUID = 1L;
これで form がセッションで管理されるようになったのでリダイレクト後も値が復帰する。
単にformをセッションで管理しただけでは、formがずっと生き残ってしまう。 これだと正規の処理が終わったにもかかわらず、前回の入力値が復活してしまいおかしいことになる。
そこでactionメソッドに
@Execute(validator=true, input="hoge?redirect=true", saveErrors=SaveType.SESSION, removeActionForm=true) public String hoge(){
removeActionForm
を true にセットしてメソッドが「正常終了」したときは form を削除するという処理にする。
↑のようにリダイレクトで編集に押し返すとはじめから編集なのかリダイレクトによる復元かがわからなくなってしまい 間違っているけど途中まで入力した値が復元できなくなってしまう。
そこでエラーメッセージの有無でリダイレクトによる復元なのか最初の編集なのかを判断する。
SAStrutsでは
if(ActionMessagesUtil.hasErrors(request)){ //ここにエラーがあった場合の処理 }
な感じでエラーメッセージの有無を確かめられるというのだが今はリダイレクトを使った場合エラーメッセージはrequestにではなくて、セッションに入っている。なのでこのメソッドでは判定できない
そこでhasErrorsメソッドをパクってsession用のhasErrorsを作る。
ActionMessages errors = (ActionMessages) session.getAttribute(Globals.ERROR_KEY); if (errors != null && !errors.isEmpty()) { //ここなら編集 }else{ //ここなら復元 }
このようにセッション中のGlobals.ERROR_KEYからエラー群を取得することができるのでこれで判定する。 オリジナルのActionMessagesUtil.hasErrorsも同様な処理を行なっている。
入力チェックにデータベースの値が必要で、 Action クラスにインジェクションされている Service のインスタンスを使いたい場合は Action クラス中にチェックメソッドを書くと楽だ。
Action クラスには主に表示する画面を組み立てるメソッドと、 組み立てた画面からのリクエストを受けて更新処理を行うメソッドの二種類があると思うが、
これとは別に検証用のメソッドを作る。
リクエストに対しどの検証用メソッドを使うかはExecuteアノテーションの引数validate
の値に文字列でメソッド名を指定する。
@Execute(validator=true, validate="validateDoHoge", input="{id}?redirect=true") public String doHoge(){ //何かの処理 } public ActionMessages validateDoHoge(){ //ここはActionクラス中なのでインジェクションされたserviceインスタンスが使える ActionMessages errors = this.validateBase(); if(StringUtils.isEmpty(hogeForm.age)){ errors.add("age" , new ActionMessage("年齢は必須", false)); } return errors; } public ActionMessages validateBase(){ ActionMessages errors = new ActionMessages(); if(StringUtils.isEmpty(hogeForm.name)){ errors.add("name" , new ActionMessage("名前は必須", false)); } return errors; }
検証用メソッドはActionMessagesインスタンスをリターンするように作る。 同じActionクラスでは同じActionFormを使うので検証方法も似てくると思うので再利用を考えて2段構えにしておく。
内部でActionMessagesをnewしてformの値に問題があった場合addでActionMessageインスタンスを追加している。 つまりこのActionMessagesインスタンスの中身が空っぽならばエラー無しというわけだ。
addメソッドは引数を2個取る。第一引数はどんな名前でエラーを登録するか。第二引数はそのメッセージを表すActionMessageインスタンスを渡す。
errors.add("name" , new ActionMessage("名前は必須", false));
通常は単に文字列を渡したいだけだと思うのでコンストラクタの第一引数にメッセージそのものの文字列、第二引数にfalseを渡す。
この場合name
という名前で登録しているのでエラーが発生した場合、表示のJSP側で
<html:errors property="name" />
という形で取り出すことができる。
通常ならばアノテーションでのチェック→独自チェックと動く。 ここでアノテーションでのチェックで引っかかってしまうとそこで処理が終了してしまって 独自チェックメソッドが動かなくなってしまう。
つまり入力値に問題がありその判定をメソッド側でやっていた場合、そのエラーが画面に反映されないということになってしまう。
なので そこで対象のメソッドに
@Execute(validator = true, validate = "validateDoHoge", input = "{id}?redirect=true", stopOnValidationError = false)
stopOnValidationError = false
を設定するとアノテーションでのチェックで止まらずに独自チェックメソッドまで処理が流れてくるようになる。
この方式は、Action側で値を判定するときもアノテーションでのチェックが前提にできないので、結局アノテーションでのチェックと同様のチェックを Action 側でも記述することが多くなってしまうので、なんだかスマートじゃない。
いちおうやり方はあるのだが、この方法だとなぜかエラーがセッションに格納されないのでちょっと放置・・・
このパターンを実装する状況としては、エラーなりに正常系での動きで画面は出すんだけど、 エラーメッセージだけは欲しいという場合が多いと思われる。
なのでinputは正常系のJSPをリダイレクト指定無しで行うはずだ。 この場合はエラーをセッションに格納する必要は無いので格納されなくてもいいのかな〜
LongRange に LongType が含まれているので併用する必要がない。
SAStruts の値検証用のアノテーションを付けたフィールドは org.seasar.struts.validator.S2FieldChecks
の中で検証される。
こいつは内部で 一部を除いて org.apache.commons.validator.GenericValidator
の実装を使っているのでこいつを使えばよい
対応
method | annotation | memo |
---|---|---|
GenericValidator.isBlankOrNull | Required | null, 空文字, 連続半角スペース、水平タブ文字 で true、全角スペースはfalse |
GenericValidator.isInt | IntegerType | Integer に変換できたら true。 null は false |
GenericValidator.isLong | LongType | |
GenericValidator.isDouble | DoubleType | |
GenericValidator.isDate | DateType | |
GenericValidator.isInRange | IntRange | |
GenericValidator.isInRange | LongRange | |
GenericValidator.isInRange | DoubleRange | |
GenericValidator.isCreditCard | CreditCardType | |
GenericValidator.isEmail | EmailType | |
GenericValidator.isUrl | UrlType | 挙動が怪しくてちゃんと判定しているようにみえない |