Spring Framework Advent Calendar 2011 part.8 - Spring MVC で String 配列にカンマ入り文字列を渡すと分割されてしまう問題
Spring framework Advent Calender 8 日目です。本日は Spring MVC に戻って、配列型のチェックボックスにまつわる話題です。
こんな画面を想像してください。
よくある Web の入力フォームです。チェックボックスの部分に注目してください。この部分は、対応する Form に配列が使われています。JSP のソースと、対応する Form のソースを見てみましょう。
... <div class="field"> <div class="label">アイテム</div> <form:checkboxes items="${names}" path="items" delimiter=" "/> </div> <div class="button"> <input type="submit" name="execute" value="登録" /> </div> ...
import java.io.Serializable; @SuppressWarnings("serial") public class RegisterForm implements Serializable { ... private String[] items; ... public String[] getItems() { return items; } public void setItems(String[] items) { this.items = items; } }
このとき、生成されるHTMLのソースは以下のようになっています(長過ぎる行には部分的に改行を入れています)。
<div class="label">アイテム</div> <span><input id="items1" name="items" value="orange" type="checkbox"><label for="items1">orange</label></span> <span> <input id="items2" name="items" value="apple" type="checkbox"><label for="items2">apple</label></span> <span> <input id="items3" name="items" value="melon" type="checkbox"><label for="items3">melon</label></span> <span> <input id="items4" name="items" value="gra,pe" checked="checked" type="checkbox"><label for="items4">gra,pe</label></span> <input name="_items" value="on" type="hidden"></div> <div class="button"> <input name="execute" value="登録" type="submit"> </div> </form></div>
これらのチェックボックスをチェックしたとき、配列に渡る値はどのようになるでしょうか? 今回のエントリのタイトルから、読者の方は既にお分かりだとは思いますが...。
特に問題なさそうです。では、チェックを1つにしてみます。
おや? "gra,pe" が "gra" と "pe" に分割されてしまっていますね。
これは、Spring が Bean にリクエストの値をバインドするときに動作する ConversionService に原因があります。Spring MVC のリクエストマッピングは、HTTP でとんでくるリクエスト文字列をさまざまな型に変換して Bean にセットする仕組みになっています。その型変換をおこなうのが ConversionService なのですが、そのうちのひとつに StringToArrayConverter があります。これはカンマ区切り文字列を String 配列に置き換える Converter ですが、これが今回、こちらの意図しない動作をした原因です。
原因がわかれば対処法はあります。今回は、カスタムの PropertyEditor を登録する方法で回避してみます。
/** * カンマを分割しない {@link StringArrayPropertyEditor}. * <p> * 配列型のフォームに対して カンマを含む String のデータを渡すと、 * StringToArrayConverter が動いて値が分割されてしまう問題の回避策。 * * @see StringArrayPropertyEditor */ public class CommaIgnoredStringArrayEditor extends PropertyEditorSupport { @Override public void setAsText(String text) throws IllegalArgumentException { String[] array = new String[] { text }; setValue(array); } }
そして、この仕掛けが必要となる Controller の initBinder メソッドでカスタムエディタを追加します。
@InitBinder public void initBinder(WebDataBinder binder) { binder.registerCustomEditor(String[].class, new CommaIgnoredStringArrayEditor()); } }
こんどは大丈夫なようですね。
なお、今回のような問題は、
- 配列型のプロパティで、
- 画面側での選択が1つしかない
- かつ、その選択した値にカンマが含まれている
という特殊な状況でしか起こりませんので、このような対処が必要になること自体あまりないかもしれません。ただ、こうして Spring の拡張ポイントを覚えていくのもまた一興かな、とも思います。