まえがき
- シンプルを目指すので、<form:form> 形式のタグライブラリ不使用にしたい
- form拡張タグは便利で良いのですが、独自の書き方を覚える手間があるので
- 自分で作る Form オブジェクトに値を入れたい(パラメータ個別にしない)
- エラーハンドリングもなるべくシンプルに
- エスケープ処理は自前で c:out または fn:escapeXml
- 試行錯誤して何とかなりそうなので忘備録として残します
環境
- Spring framework 4 (古い...)
- Bootstrap 3 (古い...)
- JDK 1.8
- JSP
作り方
- URL (例)
- /contextPath/inputAction/ : Form 表示URL
- /contextPath/nextAction/ : Form の post 先 URL
- /contextPath/finishAction/ : 完了画面
form 準備 (html/jsp)
inputAction.jsp
<form id="form" method="post" action="/contextPath/nextAction/" class="form-horizontal">
<div class="row row-center">
<div class="col-xs-10">
<div class="form-group">
<label for="param1" class="control-label col-xs-2">パラメータ1</label>
<div class="col-xs-8">
<input id="param1" name="param1" type="text"
class="form-control" value="${fn:escapeXml(form.param1)}">
<%-- エラー時メッセージ表示用 --%>
<c:if test="${!empty errorMap['param1']}">
<div style="text-align:left"><small class="text-danger"><c:out value="${errorMap['param1']}"/></small></div>
</c:if>
</div>
</div>
</div>
<div class="col-xs-10">
<div class="form-group">
<label for="param2" class="control-label col-xs-2">パラメータ2</label>
<div class="col-xs-8">
<input id="param2" name="param2" type="text"
class="form-control" value="${fn:escapeXml(form.param2)}">
<%-- エラー時メッセージ表示用 --%>
<c:if test="${!empty errorMap['param2']}">
<div style="text-align:left"><small class="text-danger"><c:out value="${errorMap['param2']}"/></small></div>
</c:if>
</div>
</div>
<div class="form-group">
<div class="col-xs-offset-2 col-xs-10" style="text-align:left">
<button id="formSubmit" name="formSubmit" type="submit" class="btn btn-primary">登録</button>
</div>
</div>
</div>
</div>
</form>
- ${form} は後ほど説明します Form オブジェクト
- ${errorMap} はエラー内容が入ってくる Map です
- escapeXml を自前でやる必要あり
- とてもシンプルにできます。パラメータが増えたら同様に増やすだけ。
CustomForm クラス
CustomForm.java
public class CustomForm {
private Long param1;
private String param2;
private Map<String, Object> errorMap;
// コンストラクタ
public CustomForm() {
errorMap = Collections.synchronizedMap(new MultiValueMap<String, Object>());
}
// バリデーション
public boolean validate() {
if (param1 == null || param1 < 0) {
errorMap.put("param1", "パラメータ1が正しくありません。");
}
if (StringUtils.isBlank(param2) || StringUtils.length(param2) > 50) {
errorMap.put("param2", "パラメータ2の長さが1~50文字ではありません。");
}
if (errorMap.isEmpty()) {
return true;
}
return false;
}
// getter,setter,etc...
}
- org.apache.commons.collections4.map.MultiValueMap をエラーマップにしているのでエラー内容を複数設定可能
inputAction 用 メソッド
CustomFormController.java
@RequestMapping(value = "/inputAction/", method = RequestMethod.GET)
public String inputAction(
Model model, HttpServletRequest request) {
return inputActionInternal(null, model, request);
}
public String inputActionInternal(CustomForm errorForm,
Model model, HttpServletRequest request) {
// 何か処理があれば追加…
// 初回表示用
if (errorForm == null) {
CustomForm form = new CustomForm();
model.addAttribute("form", form);
}
else {
// エラー表示用
model.addAttribute("form", errorForm);
}
return "inputAction";
}
- エラー時の Form オブジェクトを表示するためにメソッドを2つに分けています
nextAction 用 メソッド
CustomFormController.java
@RequestMapping(value = "/nextAction/", method = {RequestMethod.GET,RequestMethod.POST})
public Object nextAction(@ModelAttribute CustomForm form,
Model model, HttpServletRequest request, HttpServletResponse response, RedirectAttributes attributes) {
// バリデーション
if (!form.validate()) {
// 入力エラー
return inputActionInternal(form, model, request);
}
// データ登録など…
// 完了画面へリダイレクト
RedirectView redirect = new RedirectView("/finishAction/");
attributes.addFlashAttribute("form", form);
redirect.setContextRelative(true);
redirect.setExposeModelAttributes(false);
return redirect;
}
完了画面は表示するだけなので省略します。
<form:form> 形式じゃなくても割と使えます。
以上、お疲れさまでした!