- ウェブサービスはフォーム入力時にエラーが発生すると、最初からフォームを再入力するようにしてはならず、顧客が入力したデータを維持した状態でどんなエラーが発生したのか親切に知らせなければならない。
- これに関してスプリングでは、Binding ResultとBean Validationを提供し、便利に検証処理を行うようにする。
検証の種類は3つがある。
検証の種類 | 説明 |
---|---|
1.タイプ検証 | タイプによる検証 ex.数字入力をすべきところに文字を入力していないか |
2. フィールド検証 | フィールドによる検証 ex.商品名:必須、空白X 価格:1000以上100万以下 数量: 最大 9999 |
3.特定フィールドの囲を超える検証 | 複合的な条件を考慮して検証 ex.価格*数量の合計が10,000ウォン以上なのか |
BindingResult
- 検証する@ModelAttribute のすぐ後ろに記す。
- Binding Resultを使用すると、1.タイプ検証でエラーが出てもエラーなしにコントローラが呼び出され、@ModelAttributeに対してBindingされた結果がBinding Resultに盛り込まれる。
- 以後2.フィールド検証及び3.特定フィールドの範囲を超える検証ができるようにする。
@PostMapping("/add")
public String addItem(@ModelAttribute("item") ItemSaveForm form,
BindingResult bindingResult) {
// 2. フィールド検証
if (!StringUtils.hasText(item.getItemName())) {
bindingResult.rejectValue("itemName", "required");
}
if (item.getPrice() == null || item.getPrice() < 1000 || item.getPrice() >1000000) {
bindingResult.rejectValue("price", "range", new Object[]{1000, 1000000}, null);
}
// 3.特定フィールドの囲を超える検証|複合的な条件を考慮して検証
if (item.getPrice() != null && item.getQuantity() != null) {
int resultPrice = item.getPrice() * item.getQuantity();
if (resultPrice < 10000) {
// rejectメソッドでエラーメッセージを作成
bindingResult.reject("totalPriceMin"
, new Object[]{10000, resultPrice}, null);
}
}
// 検証に失敗すると、再入力フォームへ
if (bindingResult.hasErrors()) {
return "/addForm";
}
...
return "redirect:/";
}
<section id="error" th:if="${#fields.hasGlobalErrors()}">
<p th:each="err : ${#fields.globalErrors()}" th:text="${err}"></p>
</section>
<!--#fields:Binding Resultが提供する検証エラーにアクセスできる。 -->
<!--globalErrors:"totalPriceMin"のようなグローバルエラーである。 -->
<!--参考までに、"itemName"、"price"は当該フィールドに対するエラーである。-->
BeanValidation
- Annotationを通じて便利に2.フィールド検証をより簡単にできるようにする。
build.gradleに追加
implementation 'org.springframework.boot:spring-boot-starter-validation'
import org.hibernate.validator.constraints.Range;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
public class Item {
@NotNull
private Long id;
// 空値 + 空白のみの場合を許可しません。
@NotBlank
private String itemName;
// nullを許可しません。
@NotNull
// minとmaxの範囲内の値でなければなりません。
// Rangeはhibernateでのみ動作します。 (import参考)
@Range(min = 1000, max = 1000000)
private Integer price;
// メッセージを使ってメッセージを決めることもできます。
@NotNull(message = "nullX")
// 最大9999まで許可します。
@Max(value = 9999)
private Integer quantity;
...
}
- @ModelAttribute または @Request Body の前に @Validated を付ける。
@PostMapping("/add")
public String addItem(@Validated @ModelAttribute Item item
, BindingResult bindingResult
, RedirectAttributes redirectAttributes) {
...
// 既存に長く書いた以下のコードを作成しなくてもよい。
// 2.フィールド検証
// if (!StringUtils.hasText(item.getItemName())) {
// bindingResult.rejectValue("itemName", "required");
// }
// if (item.getPrice() == null || item.getPrice() < 1000 || item.getPrice() >1000000) {
// bindingResult.rejectValue("price", "range", new Object[]{1000, 1000000}、 null);
// }
//検証に失敗すると、再度入力フォームへ
if (bindingResult.hasErrors()) {
return "/addForm";
}
...
return "redirect:/";
}
<form action="item.html" th:action th:object="${item}" method="post">
<input type="text" th:field="*{itemName}" th:errorclass="field-error">
<div class="field-error" th:errors="*{itemName}">
<input type="text" th:field="*{price}" th:errorclass="field-error">
<div class="field-error" th:errors="*{price}">
</form>
- th:errorclass
- th:fieldで指定したフィールドにエラーがあれば、class情報を追加する。
- th:errors
- 該当フィールドにエラーがある場合にタグを出力する。 th:ifの便宜バージョン
reject(), rejectValue()
- 上記の例を見ると、メッセージを作成するためにreject()とrejectValue()を使用していた。
-
reject()
- 3.特定フィールドの範囲を超える検証に使用する
bindingResult.reject( "totalPriceMin", new Object[]{10000, resultPrice}, null);
errors.properties totalPrice Min=全体の価格は{0}以上である必要があります。 現在値 = {1} 参考までに、errors.propertiesを使用する場合、application.propertiesにerrorを追加しなければならない。 spring。messages.basename=messages,errors
-
rejectValue()
- 2.フィールド検証に使用する。
bindingResult.rejectValue( "price", "range", new Object[]{1000, 10000000}, null);
errors.properties NotBlank.item.itemName=商品名を書いてください。 NotBlank={0}空白X {0}はフィールド名 Range={0}、{2}~{1}の許容 Max={0}、最大{1}
エラーコード
- 上記の例を見ると、以下のようにエラーコードを作成していた。
- NotBlankのように汎用性を持って作成することもでき、NotBlank.item.itemNameのように細かく作成することもできる。 細かく作成したNotBlank.item.itemNameが優先順位を持つようになり、なければNotBlankを使うことになる。
errors.properties NotBlank.item.itemName=商品名を書いてください。 NotBlank={0}空白X {0}はフィールド名
- オブジェクトエラーの場合、code.objectName の順に作成する。
- ex. required.item
- フィールドエラーの場合、code.object Name.field / code.field / code.field Type / code の順に作成する。
- ex. NotBlank.item.itemName / NotBlank。itemName / NotBlank。java.lang.String / NotBlank