ぺんぎんらぼ

お笑いとマンガ好きなしょぼしょぼWeb系エンジニアの日記です。たまに絵を描きます。

お笑いとマンガ好きなしょぼしょぼWeb系エンジニアの日記です

Spring Bootで作るWebアプリケーション⑤ - フォームを使ってみる

前回では、Spring Bootを使ったWebアプリケーションで入力されたデータを画面間で受け渡す方法を解説しました。 今回は、入力されたデータのバリデーション(入力チェック)を解説する予定でしたが、その前に入力データをフォームオブジェクトで扱う方法を解説します。

フォームとは

画面で入力されたデータ、画面に表示するデータを一つのオブジェクトとしてまとめたものになります。 前回の実装では、コントローラーメソッドの引数で入力されたデータを受け取っていました。

    public String hello(@RequestParam("name") String name, @RequestParam("age") String age, Model model) {

この方法だと、画面の入力項目の数分、引数を定義する必要があります。例えば、個人情報を入力するような画面では、苗字、名前、住所、電話番号、性別、生年月日など、入力項目が十数個になることも珍しくありません。 画面の入出力項目をフォームにまとめることで、項目の増減に対応できるようになりますし、プログラムの見通しもよくなります。

今回は、前回までに作成したWebアプリケーションを改修して、画面で入力したデータをフォームに格納し、フォームから入力したデータを表示します。

フォームクラスの作成

まずは、画面の入出力項目を格納するフォームクラスを作成します。 難しいことはなく、入出力項目をフィールドと、そのフィールドのgetter/setterメソッドを定義したクラスを作成します。 今回は入力項目として「名前」と「年齢」があるので、名前に対応するフィールド「name」をString型で、年齢に対応するフィールド「age」をInteger型で定義します。

public class ProfileForm {

    private String name;

    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
コントローラークラスの修正

次にコンロトーラークラスの修正です。前回の開設で、画面間のデータの受け渡し方法には「必要なデータをリクエストに含めて引き継ぐ」方法と、「必要なデータをサーバに保存する」方法の2通りがありました。今回は後者の「必要なデータをサーバに保存する」方法を使用して、フォームをHTTPセッションに保存します。

@Controller
@RequestMapping("profile")
@SessionAttributes("profileForm")
public class ProfileController {

    @ModelAttribute("profileForm")
    public ProfileForm setProfileForm() {
        return new ProfileForm();
    }

    @GetMapping("name")
    public String name() {
        return "name";
    }

    @PostMapping("age")
    public String age(ProfileForm profileForm) {
        return "age";
    }

    @PostMapping("hello")
    public String hello(ProfileForm profileForm, SessionStatus sessionStatus) {
        sessionStatus.setComplete();
        return "hello";
    }
}

フォームをHTTPセッションに保存するためのコードは以下の箇所になります。

    @ModelAttribute("profileForm")
    public ProfileForm setProfileForm() {
        return new ProfileForm();
    }

このメソッドにより、setProfileFormメソッドの復帰値であるProfileFormオブジェクトが"profileForm"という名前でモデルに保存されます。

@SessionAttributes("profileForm")

そして、@SessionAttributesアノテーションにより、名前が"profileForm"のオブジェクトがHTTPセッションに保存されます。

そして、送信されたデータをフォームで受け取るためのコードは以下の箇所になります。

    @PostMapping("age")
    public String age(ProfileForm profileForm) {
        return "age";
    }

    @PostMapping("hello")
    public String hello(ProfileForm profileForm, SessionStatus sessionStatus) {
        sessionStatus.setComplete();
        return "hello";
    }

前回までは、引数で直接データを受信していましたが、今回は引数にフォームクラスを指定しています。 送信データの名称と、フォームのフィールド名が同じものが紐づけられ、送信データがフォームに格納されます。 メソッドの引数にフォームクラス名を指定していますが、メソッドの中で引数を参照していません。しかし、引数に指定することで送信データがHTTPセッションに保存されているフォームオブジェクトに設定されるので、必要な引数となります。

また、今回の趣旨とは違いますが、メソッドに指定しているアノテーションを変更しています。 前回は@RequestMappingアノテーションを使用して、パスとHTTPメソッドを指定しました。

@RequestMapping(path = "age", method = RequestMethod.POST)

今回は@PostMappingアノテーションに変更しています。この@PostMappingアノテーションは@RequestMappingアノテーションの合成アノテーションであり、@RequestMapping(method = RequestMethod.POST)を指定したことと同じになります。

@PostMapping("age")
画面の修正

コントローラクラスを修正することで、画面の入力データがフォームに格納されるようになりました。 次にテンプレートファイルを修正して、フォームから画面へ出力されるようにします。

年齢入力画面では、一つ前の名前入力画面で入力された名前を出力しているので、その名前をフォームから取得できるように修正します。

<!DOCTYPE html>
<html lang="ja" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Age</title>
</head>
<body>
<form th:action="@{/profile/hello}" th:object="${profileForm}" method="post">
    <p>
        <label for="age"><span th:text="*{name}">あなた</span>さんの年齢を入力してください</label>
        <input type="text" name="age" id="age">
    </p>
    <p>
        <input type="submit" value="次へ">
    </p>
</form>
</body>
</html>

修正箇所は2か所です。

<form th:action="@{/profile/hello}" th:object="${profileForm}" method="post">

formタグにth:object属性でフォームオブジェクトを指定します。th:object属性でオブジェクトを指定すると、以降でオブジェクトのフィールドにアクセスしやすくなります。

<span th:text="*{name}">あなた</span>

そして、名前を出力する部分も変更があります。ぱっと見、変更箇所がわかりにくいですが、th:text属性の値を変更しています。変更前は「"${name}"」でしたが、「"{name}"」に変更しています。「${~)」では、モデルを直接参照になりますが、「{~)」ではth:object属性で指定されたオブジェクトのフィールドの参照になります。

同様に、ようこそ画面も修正します。

<!DOCTYPE html>
<html lang="ja" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Hello</title>
</head>
<body th:object="${profileForm}">
    <p>ようこそ<span th:text="*{name}">あなた</span>さん</p>
    <p><span th:text="*{age}">20</span>歳なんですね。</p>
</body>
</html>
次回予告

今回は画面の入出力項目をフォームにまとめました。 次回こそは、入力チェック処理を実装します。

では、次回の「Spring Bootで作るWebアプリケーション⑥ - 入力バリデーション」でお会いしましょう!!