サンプル2の商品検索画面で、
入力された値をチェックする処理を追加する。
また初期値のセットも行う。
商品検索画面で入力された値はコマンドクラス"ProductSearch"の
それぞれ対応するプロパティにセットされる。
たとえば商品番号はProductSearch.idにセットされる。
ここでidの型はIntegerなので画面から"a"などの文字を入力すると、
Integerに変換できずにエラーが発生する。
このエラーを画面に表示するために
productSearch.jspに以下のタグを追加する。
--- 省略 --- <form:form commandName="productSearch"> <table> <tr> <th>商品番号</th> <td><form:input path="id" /></td> <td><font color="red"><form:errors path="id" /></font></td> </tr> <tr> <th>カテゴリ番号</th> <td><form:input path="categoryId" /></td> <td><font color="red"><form:errors path="categoryId" /></font></td> </tr> <tr> <th>商品名</th> <td><form:input path="productName" /></td> <td><font color="red"><form:errors path="productName" /></font></td> </tr> </table> <br> <input type="submit" value="検索" /> </form:form> --- 省略 ---
ためしに全項目に"a"と入力して検索ボタンを押す。
商品番号とカテゴリ番号はIntegerなのでエラーメッセージが表示されている。
商品名は文字列なのでエラーは表示されていない。
次にこのエラーメッセージを独自のメッセージに変更する。
WEB-INF/srcフォルダにmessages.propertiesという名前で
以下のような内容のプロパティファイルを作成する。
propertyエディタがない場合はメモ帳等で作成し、native2asciiでUnicode変換しておく。
{0}の部分がプロパティ名に置き換わって表示される。
typeMismatch={0} は無効な入力です。
変換後の内容は以下のようになる。
typeMismatch={0} \u306f\u7121\u52b9\u306a\u5165\u529b\u3067\u3059\u3002
このファイルを参照するようにapplicationContext.xmlにmessageSourceを追加する。
--- 省略 --- <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basename"><value>messages</value></property> </bean> </beans>
結果は以下のようになる。
messages.propertiesの内容を以下のように
typeMismatch.コマンド名.プロパティ名とすると、対応する項目に表示される。
typeMismatch={0} は無効な入力です。 typeMismatch.productSearch.id=商品番号は数値を入力してください。 typeMismatch.productSearch.categoryId=カテゴリ番号は数値を入力してください。
messages.propertiesの内容を以下のように
typeMismatch.データ型とすると、対応する型のプロパティに表示される。
ただし項目ごとのメッセージが指定されている場合は、そちらが優先される。
typeMismatch={0} は無効な入力です。 typeMismatch.productSearch.id=商品番号は数値を入力してください。 typeMismatch.productSearch.categoryId=カテゴリ番号は数値を入力してください。 typeMismatch.long=long型の値を入力してください。
試しに、商品検索画面とコマンドクラスにlong型のプロパティを追加し、
エラーを起こさせてみる。
Date型のデーターを入力するにはバインダにCustomDateEditorを登録する必要がある。
そのためには、ProductSearchControllerクラスでinitBinderメソッドをオーバーライドする。
以下の例では入力形式として"yyyy/MM/dd"を指定し、登録している。
protected void initBinder(HttpServletRequest req, ServletRequestDataBinder binder) throws Exception { DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd"); CustomDateEditor cde = new CustomDateEditor(dateFormat,true); binder.registerCustomEditor(Date.class, cde); }
試しに、商品検索画面とコマンドクラスにDate型のプロパティを追加し、
エラーを起こさせてみる。
画面から入力された値が、コマンドクラスのプロパティにセットされた後の
値のチェックを追加する。
例えば値が大きすぎたり小さすぎたりしないか、
必須入力が入力されているかなどである。
試しにカテゴリ番号を必須入力とし、値が入力されていない場合はエラーとしてみる。
まずmessages.propertiesファイルにエラーコードとエラーメッセージを追加する。
typeMismatch={0} は無効な入力です。 typeMismatch.productSearch.id=商品番号は数値を入力してください。 typeMismatch.productSearch.categoryId=カテゴリ番号は数値を入力してください。 typeMismatch.long=long型の値を入力してください。 error.requiered=必須入力です。
次に値を検証するValidatorクラスを作成する。
Validatorクラスを作成するにはorg.springframework.validation.Validatorインターフェースを
implementsし、以下のように各メソッドを実装する。
クラス名はProductSearchValidatorとする。
package com.ziqoo.biz.validator; import org.springframework.validation.Errors; import org.springframework.validation.Validator; import com.ziqoo.web.command.ProductSearch; public class ProductSearchValidator implements Validator { public boolean supports(Class clazz) { return ProductSearch.class.equals(clazz); } public void validate(Object obj, Errors errors) { ProductSearch ps = (ProductSearch)obj; if (ps.getCategoryId() == null) { errors.rejectValue("categoryId", "error.requiered"); } } }
supportsメソッドでは、引数のclazzがサポートするクラスの場合trueを返す。
ProductSearchValidatorはProductSearchクラスのValidatorなので、
上記のようにする。
validateメソッドでは入力値を検証する。
上記ではカテゴリIDがnullの場合、引数のerrorsへrejectValueメソッドを使用して、
エラーのあるプロパティ名とエラーコードをセットしている。
次はこのクラスをapplicationContext.xmlへ登録する。
--- 省略 --- <bean id="productManager" class="com.ziqoo.biz.product.ProductManager"> <property name="prodDao" ref="productDao"/> </bean> <bean id="productSearchValidator" class="com.ziqoo.biz.validator.ProductSearchValidator"/> <!-- メニュー項目 --> <bean id="menuItem1" class="com.ziqoo.biz.menu.MenuItem"> <property name="itemName"><value>商品検索</value></property> <property name="href"><value>productSearch.htm</value></property> </bean> --- 省略 ---
最後にこのValidatorをコントローラクラスのvalidatorプロパティにセットするため
springapp-servlet.xmlファイルに以下のように追記する。
--- 省略 --- </bean> <bean id="productSearchController" class="com.ziqoo.web.controller.ProductSearchController"> <property name="sessionForm"><value>true</value></property> <property name="commandName"><value>productSearch</value></property> <property name="commandClass"><value>com.ziqoo.web.command.ProductSearch</value></property> <property name="validator"><ref bean="productSearchValidator"/></property> <property name="formView"><value>productSearch</value></property> <property name="successView"><value>productList</value></property> <property name="productMan"> <ref bean="productManager"/> </property> </bean> --- 省略 ---
下図のように、カテゴリIDに何も入力せずに検索ボタンを押すと、
エラーが表示される。
コントローラクラスのformBackingObjectメソッドで初期値をセットしたコマンドクラスを
返すようオーバライドすると、その値が初期値として画面に表示される。
試しに商品一覧画面から商品検索画面に戻ってきたとき、前回入力した検索キーが残るようにしてみる。
以下ではコマンドクラスをsessionに保存し、次回からはsessionに保存された値を返すようにしている。
WebUtilsはorg.springframework.web.util.WebUtilsクラスを使用している。
--- 省略 --- protected Object formBackingObject(HttpServletRequest request) throws Exception { Object command = WebUtils.getSessionAttribute(request, "productSearch"); if (command == null) { command = new ProductSearch(); WebUtils.setSessionAttribute(request, "productSearch", command); } return command; } --- 省略 ---
コマンドクラス以外にも画面に表示するモデルを使用したい場合は
コントローラクラスのreferenceDataメソッドをオーバーライドする。
試しにカテゴリ番号をセレクトボックスに変更し、
その内容をここで生成するようにしてみる。
その前にまずカテゴリ情報を保持するCategoryクラスを作成する。
package com.ziqoo.biz.product; public class Category { private Integer id; private String categoryName; public String getCategoryName() { return categoryName; } public void setCategoryName(String categoryName) { this.categoryName = categoryName; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } }
次にProductSearchControllerクラスへreferenceDataメソッドを追加する。
本来であれば、カテゴリ情報はDBから検索して取得するべきかもしれないが、
サンプルなので、ソース内で直接セットする。
--- 省略 --- protected Map referenceData(HttpServletRequest arg0) throws Exception { Listoptions = new ArrayList --- 省略 ---(); Category cate = new Category(); cate.setId(1); cate.setCategoryName("その他"); options.add(cate); cate = new Category(); cate.setId(2); cate.setCategoryName("野菜"); options.add(cate); cate = new Category(); cate.setId(3); cate.setCategoryName("くだもの"); options.add(cate); Map model = new HashMap (); model.put("options", options); return model; }
商品検索画面のJSPを以下のように修正する。
--- 省略 --- <tr> <th>カテゴリ番号</th> <td> <form:select path="categoryId" > <form:option value="" label="選択して下さい"/> <form:options items="${options}" itemValue="id" itemLabel="categoryName"/> </form:select> </td> <td><font color="red"><form:errors path="categoryId" /></font></td> </tr> --- 省略 ---
商品検索画面は以下のように表示される。
親クラスにonBindというメソッドがある。
これはバインド処理の後、検証処理の前に呼び出される。
デフォルトでは何も実装されていない。
必要に応じてオーバライドする。
同じようなメソッドにonBindAndValidateというメソッドがある。
こちらは検証処理後に呼び出される。
試しに、商品名に入力された英小文字を大文字に変換する処理をonBind()に実装してみる。
--- 省略 --- protected void onBind(HttpServletRequest request, Object command){ ProductSearch ps = (ProductSearch)command; String productName = ps.getProductName(); if (productName != null) { ps.setProductName(productName.toUpperCase()); } } --- 省略 ---
試してみる。
商品名に"abc"と入力して検索。
該当する商品はないので、一覧には表示されない。ここから検索画面へ戻る。
商品名が大文字の"ABC"になっている。