第4章 エラー実装をしてみよう

4.1 入力値チェックをしよう

Spring入門7章でエンティティのバリデーションについて学びました。今回はエンティティのバリデーションを使用して、入力値チェックをするプログラムを作成します。

バリデーションとは、モデルに用意されている、値を検査するための仕組みです。あらかじめ入力される各項目にルールを設定しておくことで、入力値がそのルールに違反していないかを調べ、すべてのルールを満たしている場合のみ値の保管などを行えるようにします。

実行結果
フォルダ構造

CSSファイルの配置
  1. 「src/main/resources」パッケージの「static」フォルダ内に「css」フォルダを作成
  2. 1 で作成した「css」フォルダ内に下記からダウンロードした「style.css」を配置

https://kanda-it-school-kensyu.com/docs/sample/text/

【ファイル名:pom.xml】(追記)
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
【ファイル名:Book.java】(追記)
package jp.co.f1.spring.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.GroupSequence;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Pattern;
	
@Entity
@Table(name = "bookinfo")
public class Book {
	public interface Group1 {}
	public interface Group2 {}
	@GroupSequence({
		Group1.class,
		Group2.class
	})
	public interface All {}

	// ISBN
	@Id
	@Column(length = 20)
	@NotEmpty(message = "ISBNを入力してください", groups = Group1.class)
	private String isbn;
	public String getIsbn() {
		return isbn;
	}

	public void setIsbn(String isbn) {
		this.isbn = isbn;
	}

	// タイトル
	@Column(length = 100, nullable = true)
	@NotEmpty(message = "タイトルを入力してください", groups = Group1.class)
	private String title;
	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	// 価格
	@Column(length = 11, nullable = true)
	@NotEmpty(message = "価格を入力してください", groups = Group1.class)
	@Pattern(regexp = "^[0-9]+$", message = "価格は数字のみで入力してください", groups = Group2.class)
	private String price;
	public String getPrice() {
		return price;
	}

	public void setPrice(String price) {
		this.price = price;
	}
}
【ファイル名:ErrorController.java】
package jp.co.f1.spring.error;

import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView;

import jp.co.f1.spring.entity.Book;
import jp.co.f1.spring.repository.BookRepository;

@Controller
public class ErrorController {
	
	/**
	 * 「/check」へGET送信された場合
	 */
	@GetMapping("/check")
	// POSTデータをBookインスタンスとして受け取る
	public ModelAndView check(@ModelAttribute Book book, ModelAndView mav) {
		// 画面に出力するViewを指定
		mav.setViewName("checkForm");
		// ModelとView情報を返す
		return mav;
	}

	/**
	 * 「/check」へPOST送信された場合
	 */
	@PostMapping("/check")
	// POSTデータをBookインスタンスとして受け取る
	public ModelAndView check(@ModelAttribute @Validated(Book.All.class) Book book, BindingResult result, ModelAndView mav) {
		// 入力エラーがある場合
		if(result.hasErrors()) {
			// エラーメッセージ
			mav.addObject("message", "入力内容に誤りがあります");
			// 画面に出力するViewを指定
			mav.setViewName("checkForm");
		} else {
			// 画面に出力するViewを指定
			mav.setViewName("checkResult");
		}
		return mav;
	}
}
【ファイル名:checkForm.html】
<!DOCTYPE html>
<html>
<meta charset="UTF-8">
<title>入力値チェックをしてみよう</title>
<link rel="stylesheet" th:href="@{/css/style.css}">

<body>
  <div id="main" class="container">
    <!-- エラーメッセージ -->
    <p class="error-msg" th:if="${message} != null" th:text="${message}"></p>
    <!-- 入力フォーム -->
    <form action="/check" method="POST" th:object="${book}">
      <table class="input-table" align="center">
        <tr>
          <th>ISBN</th>
          <td>
            <input type="text" name="isbn" th:value="*{isbn}" th:errorclass="error-msg">
            <p th:if="${#fields.hasErrors('isbn')}" th:errors="*{isbn}" th:errorclass="error-msg"></p>
          </td>
        </tr>
        <tr>
          <th>TITLE</th>
          <td>
            <input type="text" name="title" th:value="*{title}" th:errorclass="error-msg">
            <p th:if="${#fields.hasErrors('title')}" th:errors="*{title}" th:errorclass="error-msg"></p>
          </td>
        </tr>
        <tr>
          <th>価格</th>
          <td>
            <input type="text" name="price" th:value="*{price}" th:errorclass="error-msg">
            <p th:if="${#fields.hasErrors('price')}" th:errors="*{price}" th:errorclass="error-msg"></p>
          </td>
        </tr>
      </table>
      <input type="submit" value="確認">
    </form>
  </div>
</body>

</html>
【checkResult.html】
<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <title>入力値チェックをしてみよう</title>
</head>

<body>
  <p>正しく入力できていました。</p>
</body>

</html>
アプリケーションにアクセス

以下のアドレスからアプリケーションにアクセスします。
URL:http://localhost:8080/check

解説
・@Pattern

こちらは7章でも紹介されています。正規表現のパターンを指定して入力チェックを行います。引数の「regexp」という値にパターンの文字列を指定し、指定した正規表現に一致するかどうかをチェックします。今回はこの「regexp」に『”^[0-9]+$”』という正規表現を渡しています。これは、数字のみで入力されているかどうかをチェックしてくれます。正規表現は他にもあるので気になる方は調べてみてください。

・@GroupSequence

バリデーションの結果がNGになるとチェックに引っ掛かった分だけエラーメッセージが表示されてしまいます。そのため、バリデーションの実行順序を設定する必要があります。そこで使用するのがバリデーションのグループ設定です。@GroupSequenceアノテーションは、バリデーションの順番を設定します。サンプルコードでは以下の順番でチェックされます。

  • Group1
  • Group2

価格の入力チェックは①空白チェック②数字チェック(数字のみで入力されているか)の2つをチェックする必要があります。Group1を①空白チェック、Group2を②数字チェックとしてバリデーションの実行順序を設定しています。

NEXT>> 4.2 重複チェックをしよう