0
0

検証、BindingResult、BeanValidation

Last updated at Posted at 2024-02-01
  • ウェブサービスはフォーム入力時にエラーが発生すると、最初からフォームを再入力するようにしてはならず、顧客が入力したデータを維持した状態でどんなエラーが発生したのか親切に知らせなければならない。
  • これに関してスプリングでは、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'
  • @NotNull@NotBlank など検証したいものに応じてAnnotation を付ける。
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;
    ...
}
@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()を使用していた。
  1. 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
    
  2. 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
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0