SAStruts/入力チェック

SAStruts/入力チェック

つまりバリデータ(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(){

form 中のパラメータを使った押し返し設定

パラメータ付きの 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"/>

のように書く。

redirect で入力画面に押し返した後にもエラーメッセージを維持したい

通常、エラーメッセージ自体はデフォルトで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クラス中にチェック用のメソッドを作る

入力チェックにデータベースの値が必要で、 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をリダイレクト指定無しで行うはずだ。 この場合はエラーをセッションに格納する必要は無いので格納されなくてもいいのかな〜

LongType と LongRange を併用するとエラーになる

LongRange に LongType が含まれているので併用する必要がない。

Action 側で ActionForm の値検証アノテーションと同じ検証をする

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 挙動が怪しくてちゃんと判定しているようにみえない

タグ

java/sastruts/input_check.txt · 最終更新: 2020-09-30 11:43 by ore