#目的
Spring Quickstart Guideを取り組み終えた方、SpringBootを学び始めた方、復習をしたい方に向けて、
公式ガイドValidating Form Inputを実際に取り組み学んだことを共有します。
完成形はこちらになります。
不正な値が入力したままSubmitボタンが押されるとエラーメッセージを表示して、
有効な値が入力されている場合は、別の画面へと遷移出来るように実装していきます。
開発環境、これまでのおさらいは以下になります。
OS: macOS Mojave バージョン10.14.6
テキストエディタ: Visual Studio Code(以下VSCode)
Java: 11.0.2
QuickstartGuideのおさらいはこちらから
Building a RESTful Web Service編のおさらいはこちらから
Consuming a RESTful Web Service編のおさらいはこちらから
Accessing Data with JPA編のおさらいはこちらから
Handling Form Submission編のおさらいはこちらから
#1.SpringBoot projectを始めよう!
まずは、spring initializr にアクセスします。
1.ADD DEPENDENCIESボタンをクリックして、Spring Web
とThymeleaf
を追加。
2.Artifact, Nameは、validating-form-input
に変更。
3.Javaを11
に変更。
そしてGENERATE
ボタンをクリックしてZipファイルをダウンロードします。
ダウンロードしたZipファイルを展開したら準備完了です。
#2.コードを追加しよう!
先ほどのフォルダをVSCodeで開きます。
拡張機能のJava Extension Packのインストールの推奨します。と言われるのでインストールしておきましょう。
##PersonForm.javaを作成しよう!
src/main/java/com/example/validatingforminput/
にPersonForm.javaファイルを作成します。
公式を参考にコードを追加します。
package com.example.validatingforminput;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class PersonForm {
@NotNull
@Size(min=2, max=30)
private String name;
@NotNull
@Min(18)
private Integer age;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String toString() {
return "Person(Name: " + this.name + ", Age: " + this.age + ")";
}
}
追加したコードを深掘りしていきます。
###①@NotNull
と@Size
@NotNull
@Size(min=2, max=30)
private String name;
String型のname変数を宣言時に2つバリデーションのアノテーションを付与しています。
@NotNull
は、nullを許可しません。あくまでnullは許可しないだけで、空文字や空白は許可されるので注意してください。
@Size
は、()
の中で指定したmin以上、max以下であるかを検証しています。
今回は、@Size(min=2, max=30)
ですので、2文字以上30文字以下ではないとエラーになります。
###②@Min
@NotNull
@Min(18)
private Integer age;
Integer型のage変数を宣言時に2つバリデーションのアノテーションを付与しています。(1つは上述の@NotNull
)
@Min
は、()
の中に記述した値より小さいかどうか検証しています。
今回は、@Min(18)
ですので、18未満はエラーになります。
###③ゲッター/セッター、toStringメソッド
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String toString() {
return "Person(Name: " + this.name + ", Age: " + this.age + ")";
}
変数name、ageの値を取得、変更するためのゲッター/セッターメソッドを定義しています。
また、name、ageを文字列として表示するためのtoStringメソッドを定義しています。(今回使用していない気がしますが、デバッグ用なのかな?)
##pom.xmlにvalidationの依存関係を追加しよう!
公式ガイドには記載がなかったのですが、Spring Boot 2.3以上の時は、
PersonForm.java
のimport javax.validation.constraints.〇〇
がエラーになります。
(Spring Boot2.3 Relealse Notes)
以下をdependenciesに追加してください。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
##WebController.javaを作成しよう!
src/main/java/com/example/validatingforminput/
にWebController.javaファイルを作成します。
公式を参考にコードを追加します。
package com.example.validatingforminput;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Controller
public class WebController implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/results").setViewName("results");
}
@GetMapping("/")
public String showForm(PersonForm personForm) {
return "form";
}
@PostMapping("/")
public String checkPersonInfo(@Valid PersonForm personForm, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "form";
}
return "redirect:/results";
}
}
追加したコードを深掘りしていきます。
###①WebMvcConfigurerインターフェースとaddViewControllersメソッド
@Controller
public class WebController implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/results").setViewName("results");
}
// 以下略
}
WebMvcConfigurerインターフェースを実装、addViewControllersメソッドをオーバーライドしています。
http://localhost:8080/results
というURLの時、results.htmlというテンプレートを参照するように設定していると解釈しています。
URLとテンプレートがマッピングされるようにしているみたいです。results.htmlの実装は後ほど。
###②showFormメソッド
@GetMapping("/")
public String showForm(PersonForm personForm) {
return "form";
}
@GetMapping
は、http://localhost:8080/
でGETリクエストがあった時、showFormメソッドを呼ぶためのアノテーションです。
引数にPersonFormを受け取っています。メソッドの戻り値である、form.html
のform属性でPersonFormを関連づける事ができます。
form.htmlの実装は後ほど。
###③checkPersonInfoメソッド
@PostMapping("/")
public String checkPersonInfo(@Valid PersonForm personForm, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "form";
}
return "redirect:/results";
}
@PostMapping
は、http://localhost:8080/
でPOSTリクエストがあった時、checkPersonInfoメソッドを呼ぶためのアノテーションです。
1つ目の引数@Valid PersonForm personForm
は入力されたデータの検証をしています。
2つ目の引数BindingResult bindingResult
は、入力されたデータと検証結果(エラーがあるかどうか)を保持するためのアノテーションです。
bindingResult.hasErrors()
が書かれているif文では、エラーがあるかどうかを確認しています。
もしエラーがある場合、form.htmlをエラーメッセージと入力されていた値と共に再描画します。
エラーが無ければ、http://localhost:8080/results
にリダイレクトされます。
##form.htmlを作成しよう!
src/main/resources/templates/
にform.htmlファイルを作成します。
公式を参考にコードを追加します。
<html xmlns:th="http://www.thymeleaf.org">
<body>
<form action="#" th:action="@{/}" th:object="${personForm}" method="post">
<table>
<tr>
<td>Name:</td>
<td><input type="text" th:field="*{name}" /></td>
<td th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Name Error</td>
</tr>
<tr>
<td>Age:</td>
<td><input type="text" th:field="*{age}" /></td>
<td th:if="${#fields.hasErrors('age')}" th:errors="*{age}">Age Error</td>
</tr>
<tr>
<td><button type="submit">Submit</button></td>
</tr>
</table>
</form>
</body>
</html>
nameとageを入力するフォーム画面ですね。
追加したコードのthymeleafの記述について深掘りしていきます。
thymeleafとは、springbootで扱う事が出来るテンプレートエンジンです。th:〇〇と記述します。
日本語で書かれたthymeleafチュートリアルもあります!
th:action
formタグのaction属性の内容を置換しています。記述の仕方は、th:action="@{}"
です。
method="post"となっているので、Submitボタンが押された時にWebControllerのcheckPersonInfoメソッドが呼ばれます。
th:object
th:object
でオブジェクトを指定しています。これにより、オブジェクト内の変数の参照の仕方がpersonForm.nameではなく、*{name}のような記述方法が可能になります。
th:field
th:objectで指定したオブジェクト内の変数を表示するためにth:field="*{変数名}"
と記述します。
今回は、PersonFormクラスの中にname、ageがあるので、th:field="*{name}"
、th:field="*{age}"
となります。
また、th:field="*{変数名}"
の中に記述した変数名がinputのid属性とname属性になります。
th:if
th:if=条件
と記述します。trueであった場合、そのタグ、子要素が表示されます。
今回の条件は、エラーがあった場合trueになります。
th:errors
th:errors="*{変数名}"
と記述します。エラーがあった場合のエラーメッセージが表示されます。
th:ifとth:errorsでエラーの有無を確認して、エラーメッセージ表示する領域を確保している感じですね。
##results.htmlを作成しよう!
src/main/resources/templates/
にresults.htmlファイルを作成します。
公式を参考にコードを追加します。
<html>
<body>
Congratulations! You are old enough to sign up for this site.
</body>
</html>
form画面で不正な値が入力されることが無ければこちらの画面に遷移します。
#3.実行してみよう!
アプリケーション実行の準備が出来たので確認しましょう。
ターミナルで以下のコマンドを入力してEnterしてください。
$ ./mvnw spring-boot:run
そして、http://localhost:8080/
にアクセスすると以下のフォーム画面が表示されるはずです。(form.htmlが表示される)
nameに1文字、ageに18未満の数値を入力して、Submitボタンを押すとエラーメッセージが表示されます。
ageに何も入力せずにSubmitボタンを押してもエラーメッセージが表示されます。
nameに2文字以上、ageを18以上の数値を入力して、Submitボタンを押すとresult画面(result.html)が表示されます。
お疲れ様でした!完成です!
#参考サイト
Spring Bootで入力値の検証
ビュー名を返すだけのControllerなら、Controllerは別にいらないらしいよ!
検証とエラーメッセージ