JavaのWebアプリケーション開発フレームワークによる、Webサイト開発の顛末記です。

EclipseのMavenを使った、Spring-MVC、Thymeleaf、MyBatis 等のプログラミングテクニックを、
備忘録的に記録しています。実際に動くソースコードを多用して説明していますので、
これからEclipseや、Spring-MVCを始めたいと思っている人にとって、少しでも参考になれば幸いです。
■SpringMVC の小径 第7歩 Spring-MVC 迷いの一歩
7-2)入力チェック
最初は、遠く春霞に滲んでいた目的地が、あともう一歩のところまで来ました。
残るは、目の前の小高い丘の斜面を登り切れば目的地に到着です。

前の節では、データモデルのバインドと、@ModelAttributeによる
効率的なリクエストパラメータ受け渡しという、実戦的なプログラミングも習得しました。
たいぶ体力もついたし、もうどんな道だって平気で歩けるさ!
なんて油断していると、とんでもない事故に巻き込まれないとも限りません。
石につまづいて転んで怪我をするかもしれないし、
景色に見とれて足元をおろそかにすると、思わず犬のウ○チを踏んづけてしまうかもしれません。
「転ばぬ先の杖」常に危険を予測して進みましょう。
ここでは、危機の対応方法について学習します。

前節で作ったプログラムには、一つ欠点があることは既に説明しています。
年齢の入力欄に数字以外の文字を入力すると、400エラーが返され悲しい思いをしました。
こんな場合は、どうすればいいでしょうか?
考えられる対応策は?
1)ブラウザ側でJavaScriptを使って、submitする直前で入力値をチェックする。
2)サーバー側で入力値をチェックして、適切なエラー情報をviewに返す。
良くできました。
1)JavaScriptによるチェックも説明したいのですが、
そろそろ陽も少し傾きかけて時間の余裕があまりないので、
今回は、2)サーバー側でのチェック(Validation Check)に絞って説明します。

■アノテーションによる入力検証
使用するview、コントローラは前回のものを改造して流用します(ファイル名は皆前回と同じ)。
まずは view 側 入力用HTML(WebContent/promenade.html)

それぞれの入力項目に対して、入力検証を行いエラーがあったら、入力内容が破棄されないよう保持したまま、
さらに、エラーメッセージを表示する様に改造しています。

 

次は今回新しく参加した、エラーメッセージ定義ファイル
(WebContent/WEB-INF/classes/ValidationMessages.properties)

入力値の妥当性チェックは、具体的にはHibernate Validatorプラグインによってチェックされます。
1-3)pom.xmlの編集」で、hibernate-validatorプラグインを指定したのはこのためです。
具体的な実装方法はすぐ下で説明しますが、
Hibernate Validatorは、エラー時のメッセージをValidationMessages.properties
というファイル名のメッセージリソースファイルで管理しています。
Hibernateに付属のメッセージリソースファイルは英語でわかりにくいので、
私は、m4hv-extensions - で公開されている
日本語訳されたメッセージリソースファイルを使用させてもらっています。
このメッセージだけでは不十分な時は、当然自前のメッセージを追加することもできます。
今回のサンプルでは、年齢(age)に数字以外が入力された場合の型変換エラーに対するメッセージとして、
typeMismatch.int={0} は整数で入力してください。
を追加しています。
型変換エラーメッセージを有効にするために、「4-2)Spring-MVC設定」で、
Spring-MVC設定ファイル(applicationContext.xml)に、ValidatorメッセージリソースファイルのBean定義を設定しています。
この設定を行わないと、型変換エラーの場合
 Failed to convert property value of type [java.lang.String]
  to required type [java.lang.Integer] for property propertyname;
  nested exception is java.lang.NumberFormatException: For input string: "XXX"

というような長ったらしい検証エラーが表示されるので設定しておきましょう。

余談ですが、このサイトで紹介されている「m4hv-extensions」プラグインは、
日本語の入力妥当性チェックを行うことができるプラグインです。
ただ、残念ながらMavenさんの楽園には陳列されていないので、必要な場合は、
上記URLから直接ダウンロードして使用しますが、
今回は、日本語の妥当性チェックまでは行わないため割愛します。

あと補足ですが、ValidationMessages.propertiesファイルを編集する際は、Eclipseに標準で付いている
「Limy プロパティ・エディタ」で編集する事をお勧めします。
メニューになかったら、「設定」の「エディタ」-「ファイルの関連付け」で探してみてください。
テキストエディタで編集すると、後でえらいことになっても責任もてません(文字化け上等)。

 

次が今回の重要な部分、
リクエストパラメータFormBean(src/main/java/jp/dip/arimodoki/model/PromenadeForm.java)

ソース中の赤字の部分で、また何やら怪しいアノテーションが色々追加されています。
このアノテーション(プラス チェック条件)を入れておくだけで、
Spring-MVCは入力チェックを行ってくれます。
無印のアノテーションは、エラーがあった場合、先のValidationMessages.propertiesで
対応するエラーメッセージを出力します。
"message="の属性があるものは、ここで好きなエラーメッセージを指定することができます。
"message={xxxx}" の形式の場合は、好きなメッセージ+ValidationMessages.propertiesの(xxxx)で
   定義されているメッセージが表示されます。
入力検証アノテーションは、プロパティ(変数)毎に指定します。
入力検証アノテーションがないプロパティは、検証の対象外となります。

他にどんな入力検証用のアノテーションがあるのか色々ネットを探してみましたが、今のところ
Yamkazu'sさんのブログ
が、一番わかりやすいと思うので、ぜひ一度訪問してみてください。
※)前回と同じくaccessorメソッド(getXXX, setXXX)は、
  Eclipseで自動出力しているのでコメントなどが入っていません。

※)注 2017/Dec/17 追記
最近気が付いたことがあるので記録しておきます。
本編を執筆した当初 2016年時点の、hibernate-validatorプラグインのバージョンは、5.2.4.Finalでした。
現在は、6.0.2.Finalにバージョンアップされているのですが、
最近、ソースを眺めていたら、EclipseのJavaソースエディタに、
見慣れない警告マークが表示されているのに気が付きました。
何だろう? と思いソースを調査したところ、
どうも、hibernate-validatorの、@Emailや、@NotBlank アノテーションが、
いつの頃からか(おそらく5.4以降)非推奨となってしまっていました。
全く使えない訳では無いようですが、
代替えとして、以下のアノテーションを使用する様にしました。
import javax.validation.constraints.Email;    //@Email
import javax.validation.constraints.NotEmpty;  //@NotBlankの代替え
プラグインのバージョンは以下の組み合わせを用いています。
※)pom.xml抜粋:
  <dependency>
   <groupId>javax.validation</groupId>
   <artifactId>validation-api</artifactId>
   <version>2.0.0.Final</version>
  </dependency>
  <dependency>
   <groupId>org.hibernate.validator</groupId>
   <artifactId>hibernate-validator</artifactId>
   <version>6.0.6.Final</version>
  </dependency>

今後、@Email / @NotBlankアノテーションが出現する場合は、
ここを念頭に置いて、頭の中で置き換えて使うようにしてください。
FormBeanインターフェース(src/main/java/jp/dip/arimodoki/model/PromenadeFormIf.java)

PromenadeFormのインターフェースです。
PromenadeFormクラスの変更に伴いメソッド名が変更された以外は特筆するべき内容はありません。
※)ちなみにこれも前回と同じく、Eclipseで自動出力しており、自分で書いたりしていません。

 

そして、肝心要のコントローラ(src/main/java/jp/dip/arimodoki/cntl/Promenade.java)

ここも手抜きでソースファイルに直接コメント入れときましたんで、そこんとこよろしく!

前回と違うところ
1)コントローラメソッド promenade() が一個増えました。
  「http://localhost:8080/j_Labo/promenade.xhtml」で入力画面が開きます。

2)コントローラメソッド promenaderes() の引数に変更があります。 ⇒ ここ重要です。
  リクエストパラメータを受け取るFormBean
  @ModelAttribute("PromenadeFormData") PromenadeFormIf formbean は前回と同じですが、
  その引数の直前に、@Valid というアノテーションが追加されています。
  そして、formbean引数の後ろに、BindingResult result という引数が追加されています。
  入力検証を行う場合だけは、この3個の並び順は決まっていてこの通りに並べないと検証されません。
  @Validアノテーションは、formbean に対して、「入力検証をやっちゃうぞ」という宣戦布告です。
  result引数は、入力検証の戦いに負けたら放り込まれてしまう牢屋です。
  牢屋に放り込まれた敗北者は、Spring-MVCによってviewに運ばれ、そこで晒し者にされます。
  もしここで、@Validアノテーションが付いていなかったら、
  FormBean側でいくら検証用アノテーションを書こうが、
  Spring-MVCは、「そんなのは俺知らんもんね」と無視してしまうので注意してください。

3)コントローラメソッド promenaderes()の処理内容の説明
  入力検証の結果、result.hasErrors()が True の場合、何か入力エラーが発生しているので
  結果画面(promenaderes.html)を表示せずに、入力画面(promenade.html)を表示し
  ここに書いてある誓約「th:if="${#fields.hasErrors('変数名')}" th:errors="*{変数名}"」に従って、
  敗北者は、民衆の前で懺悔させられます。
  この誓約書を翻訳すると、だいたい次のような意味になります。
  「汝(変数名)、懺悔すべきことあらば(th:if)、神の御名のもとに(Thymeleaf)、申し述べよ(th:errors)」

  その下、①で囲われている部分は、ラジオボタンのチェック結果の文字列を
  結果画面に表示したいのですが、ラジオボタンプロパティは、FormBeanで配列定義してあるので
  そのままでは、結果画面に表示できないため、表示用のプロパティにコピーしています。

 

最後に view 側 入力結果表示用HTML(WebContent/promenaderes.html)

入力検証がすべて問題ない場合、この結果画面が表示されます。
前回と、表示項目が少し異なっていますが。
処理内容的には、特に新しいことは何もやっていません。

 

では、どんな動きをするかちょっとだけ確認してみましょう。
いつもと同じく、Eclise「サーバービュー」でローカルサーバーを起動し、
ブラウザのURLに「http://localhost:8080/j_Labo/promenade.xhtml」を入力します。
今回は、xhtml を指定してください。
なにか適当に入力してみて
入力画面が表示されたら、色々いぢわるな答えを書いてみてください。
まじめに回答しなさい!
真面目に回答してみましょう。
Hello! Everyone. my name is Taro Spring

 

■論理チェック
入力検証アノテーションと、@Validアノテーションによる入力チェックは解かりましたが、
例えば、よくあるログイン画面でのログインアカウント/パスワードなどの
論理チェックは、これではチェックできませんね。
この場合の検証ロジックは、入力検証が通ったあとでDBに問い合わせて初めて正常かエラーかが判明します。
このような場合の検証結果はどのように処理すればよいでしょうか?
一つの正解としては、
if (result.hasErrors()) { のチェックを行う前に
論理チェックを実施して、エラーが発生したら、result.rejectValue()を呼び出すやり方があります。
先ほどのコントローラに少し処理を追加してみました。

前回と違うところとしては、
入力された年齢をチェックして、特定の範囲の年齢はNGとするようなチェックを行い
result.rejectValue()にエラーメッセージを追加していることです。
本来だと、ここでDB問い合わせなどを行って、もう少し真面目な入力検証をやりたかったのですが
ちょっと手抜きしてしまいました。

 

いかがでしたか?
これで、もう道端に犬のウ○チが落ちていても大丈夫です。
妥当性チェックを行うプラグインはHibernate Validatorのほかにも色々あるみたいですが、
今のところ、これで十分事足りているので、正直これ以外使ったことがありません。
とにかく、たったこれだけのコードを書くだけで、きちんとチェックしてくれるので非常に助かります。
(昔はこれでずいぶん苦労したなぁ。。。ぼそ)


丘の斜面を登り切って、目前のすばらしい眺めに感動しています。
何とか、最初の目的であった、Spring-MVCの基本部分を(一応)歩き通すことができてほっとしています。
さて、今日はそろそろ陽も傾いてきました。
大事に運んできたビールでも飲みながら、暫く雄大な夕景を堪能しましょう。