はじめてのSpring MVCアプリケーション -------------------------------------------------------------- .. only:: html .. contents:: 目次 :depth: 3 :local: Spring MVCの、詳細な使い方の解説に入る前に、実際にSpring MVCに触れることで、Spring MVCを用いたWebアプリケーションの開発に対するイメージをつかむ。 | 検証環境 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 本節の説明では、次の環境で動作検証している。(他の環境で実施する際は、本書をベースに適宜読み替えて設定していくこと。) .. tabularcolumns:: |p{0.25\linewidth}|p{0.75\linewidth}| .. list-table:: :header-rows: 1 :widths: 25 75 * - 種別 - プロダクト * - OS - Windows 10 * - JVM - \ `Java `_\ 17 * - IDE - \ `Spring Tool Suite `_\ 4.17.1.RELEASE (以降「STS」と呼ぶ。設定方法は\ :doc:`../Appendix/SpringToolSuite4`\ を参照されたい。) * - Build Tool - \ `Apache Maven `_\ 3.8.6 (以降「Maven」と呼ぶ) * - Application Server - \ `Apache Tomcat `_\ 10.1.7 * - Web Browser - \ `Google Chrome `_\ 108 .. note:: インターネット接続するためにプロキシサーバーを介する必要がある場合は、\ :ref:`STSのProxy設定`\ と\ `MavenのProxy設定 `_\ が必要である。 | 新規プロジェクト作成 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ インターネットから\ ``mvn archetype:generate``\ を利用して、プロジェクトを作成する。 .. code-block:: console mvn archetype:generate -B^ -DarchetypeGroupId=com.github.macchinetta.blank^ -DarchetypeArtifactId=macchinetta-web-blank-noorm-thymeleaf-archetype^ -DarchetypeVersion=1.9.1.RELEASE^ -DgroupId=com.example.helloworld^ -DartifactId=helloworld^ -Dversion=1.0.0-SNAPSHOT ここではWindows上にプロジェクトの元を作成する。 .. code-block:: console C:\work>mvn archetype:generate -B^ More? -DarchetypeGroupId=com.github.macchinetta.blank^ More? -DarchetypeArtifactId=macchinetta-web-blank-noorm-thymeleaf-archetype^ More? -DarchetypeVersion=1.9.1.RELEASE^ More? -DgroupId=com.example.helloworld^ More? -DartifactId=helloworld^ More? -Dversion=1.0.0-SNAPSHOT [INFO] Scanning for projects... [INFO] [INFO] ------------------< org.apache.maven:standalone-pom >------------------- [INFO] Building Maven Stub Project (No POM) 1 [INFO] --------------------------------[ pom ]--------------------------------- [INFO] [INFO] >>> maven-archetype-plugin:3.1.2:generate (default-cli) > generate-sources @ standalone-pom >>> [INFO] [INFO] <<< maven-archetype-plugin:3.1.2:generate (default-cli) < generate-sources @ standalone-pom <<< [INFO] [INFO] [INFO] --- maven-archetype-plugin:3.1.2:generate (default-cli) @ standalone-pom --- [INFO] Generating project in Batch mode [INFO] Archetype repository not defined. Using the one from [com.github.macchinetta.blank:macchinetta-web-blank-noorm-thymeleaf-archetype:1.9.1.RELEASE] found in catalog remote [INFO] ---------------------------------------------------------------------------- [INFO] Using following parameters for creating project from Archetype: macchinetta-web-blank-noorm-thymeleaf-archetype:1.9.1.RELEASE [INFO] ---------------------------------------------------------------------------- [INFO] Parameter: groupId, Value: com.example.helloworld [INFO] Parameter: artifactId, Value: helloworld [INFO] Parameter: version, Value: 1.0.0-SNAPSHOT [INFO] Parameter: package, Value: com.example.helloworld [INFO] Parameter: packageInPathFormat, Value: com/example/helloworld [INFO] Parameter: package, Value: com.example.helloworld [INFO] Parameter: version, Value: 1.0.0-SNAPSHOT [INFO] Parameter: groupId, Value: com.example.helloworld [INFO] Parameter: artifactId, Value: helloworld [INFO] Project created from Archetype in dir: C:\work\helloworld [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 6.278 s [INFO] Finished at: 2021-07-20T14:49:33+09:00 [INFO] ------------------------------------------------------------------------ C:\work> STSのメニューから、[File] -> [Import] -> [Maven] -> [Existing Maven Projects] -> [Next]を選択し、archetypeで作成したプロジェクトを選択する。 .. figure:: images_FirstApplication/NewMVCProjectImport.png :alt: New MVC Project Import :width: 60% Root Directoryに \ ``C:\work\helloworld``\ を設定し、Projectsにhelloworldのpom.xmlが選択された状態で、 [Finish] を押下する。 .. figure:: images_FirstApplication/NewMVCProjectCreate.png :alt: New MVC Project Import :width: 60% Package Explorerに、次のようなプロジェクトが生成される。 .. figure:: images_FirstApplication/HelloWorldWorkspace.png :alt: workspace Spring MVCの設定方法を理解するために、生成されたSpring MVCの設定ファイル(src/main/resources/META-INF/spring/spring-mvc.xml)について、簡単に説明する。 .. code-block:: xml :emphasize-lines: 18-19, 30-31, 59-68, 70-74, 79-82 .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - \ ````\ 要素を定義することにより、Spring MVCのデフォルト設定が行われる。デフォルトの設定については、\ `Spring Framework Documentation -Enable MVC Configuration- `_\ を参照されたい。 * - | (2) - Spring MVCで使用するコンポーネントを探すパッケージを定義する。 * - | (3) - Thymeleaf用の\ ``ViewResolver``\ を指定する。ここでは、idが\ ``templateEngine``\ のbeanである(5)を参照している。 * - | (4) - Viewファイルの拡張子と配置場所を定義する。 * - | (5) - Springを用いたThymeleafの実装を定義する。またここでは、idが\ ``templateResolver``\ のbeanである(4)を参照している。 | 次に、Welcomeページを表示するためのController (\ ``com.example.helloworld.app.welcome.HelloController``\ ) について、簡単に説明する。 .. code-block:: java :emphasize-lines: 16,25,35,37 package com.example.helloworld.app.welcome; import java.text.DateFormat; import java.util.Date; import java.util.Locale; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; /** * Handles requests for the application home page. */ @Controller // (6) public class HelloController { private static final Logger logger = LoggerFactory .getLogger(HelloController.class); /** * Simply selects the home view to render by returning its name. */ @GetMapping("/") // (7) public String home(Locale locale, Model model) { logger.info("Welcome home! The client locale is {}.", locale); Date date = new Date(); DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale); String formattedDate = dateFormat.format(date); model.addAttribute("serverTime", formattedDate); // (8) return "welcome/home"; // (9) } } .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (6) - \ ``@Controller``\ アノテーションを付けることで、DIコンテナにより、コントローラクラスが自動で読み込まれる。前述「Spring MVCの設定ファイルの説明(2)」の設定により、component-scanの対象となっている。 * - | (7) - HTTPメソッドがGETで、Resource(もしくはRequest URL)が"/"で、アクセスする際に実行される。 * - | (8) - Viewに渡したいオブジェクトを\ ``Model``\ に設定する。 * - | (9) - View名を返却する。前述「Spring MVCの設定ファイルの説明(4)」の設定により、"WEB-INF/views/welcome/home.html"がレンダリングされる。 | 最後に、Welcomeページを表示するためThymeleafのテンプレートHTML (\ ``src/main/webapp/WEB-INF/views/welcome/home.html``\ ) について、簡単に説明する。 .. code-block:: html :emphasize-lines: 12 Home

Hello world!

The time on the server is 2018/01/01 00:00:00 JST.

.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (10) - | スタンダードダイアレクトが提供する属性を使用したとき、EclipseなどのIDEでの警告を抑止するため、ネームスペースを付与する。 * - | (11) - 前述の「Controllerの説明(8)」でModelに設定したオブジェクト(serverTime)は、HttpServletRequestに格納される。 そのため、テンプレートHTMLで\ ``${serverTime}``\ と記述し、Thymeleafの\ ``th:text``\ 属性を使用することで、Controllerで設定した値を画面に出力することができる。 \ ``th:text``\ 属性はHTMLエスケープをして出力を行うため、自動的にXSS対策をとることができる。 詳細については\ :ref:`xss_how_to_use_ouput_escaping`\ を参照されたい。 | サーバーを起動する ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | STSで、"helloworld"プロジェクトを右クリックして、Run As -> Run On Server -> localhost -> Tomcat v10.1 Server at localhost -> Finishを実行し、helloworldプロジェクトを起動する。 | ブラウザに "http://localhost:8080/helloworld/" を入力し、実行すると下記の画面が表示される。 .. figure:: images_FirstApplication/AppHelloWorldIndex.png :alt: Hello World | .. _first-application-create-an-echo-application: エコーアプリケーションの作成 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 続いて、簡単なアプリケーションを作成する。作成するのは、次の図のようなテキストフィールドに、名前を入力すると メッセージを表示する、いわゆるエコーアプリケーションである。 .. figure:: images_FirstApplication/AppEchoIndex.png :alt: Form of Echo Application .. figure:: images_FirstApplication/AppEchoHello.png :alt: Output of Echo Application | フォームオブジェクトの作成 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | まずは、テキストフィールドの値を受け取るための、フォームオブジェクトを作成する。 | \ ``com.example.helloworld.app.echo``\ パッケージに\ ``EchoForm``\ クラスを作成する。プロパティを1つだけ持つ、単純なJavaBeanである。 .. code-block:: java package com.example.helloworld.app.echo; import java.io.Serializable; public class EchoForm implements Serializable { private static final long serialVersionUID = 1L; private String name; public void setName(String name) { this.name = name; } public String getName() { return name; } } | Controllerの作成 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | 次に、Controllerを作成する。 | 同じく\ ``com.example.helloworld.app.echo``\ パッケージに、\ ``EchoController``\ クラスを作成する。 .. code-block:: java :emphasize-lines: 11,14,20,22,25-27 package com.example.helloworld.app.echo; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; 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.bind.annotation.RequestMapping; @Controller @RequestMapping("echo") public class EchoController { @ModelAttribute // (1) public EchoForm setUpEchoForm() { EchoForm form = new EchoForm(); return form; } @GetMapping // (2) public String index(Model model) { return "echo/index"; // (3) } @PostMapping(value = "hello") // (4) public String hello(EchoForm form, Model model) {// (5) model.addAttribute("name", form.getName()); // (6) return "echo/hello"; } } .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ``@ModelAttribute``\ というアノテーションを、メソッドに付加する。このアノテーションがついたメソッドの返り値は、自動でModelに追加される。 | Modelの属性名を、\ ``@ModelAttribute``\ で指定することもできるが、デフォルトでは、クラス名の先頭を小文字にした値が、属性名になる。この場合は、”echoForm”である。フォームの属性名は、次に説明する\ ``form:form タグ``\ の\ ``modelAttribute``\ 属性の値に一致している必要がある。 * - | (2) - | メソッドに付加した\ ``@GetMapping``\ アノテーションの\ ``value``\ 属性に何も指定しない場合、クラスに付加した\ ``@RequestMapping``\ のルートにマッピングされる。この場合、"/echo"にGETメソッドを使用してアクセスすると、\ ``index``\ メソッドが呼ばれる。 * - | (3) - | View名で"echo/index"を返すので、ViewResolverにより、 "WEB-INF/views/echo/index.html"がレンダリングされる。 * - | (4) - | メソッドに付加した\ ``@PostMapping``\ アノテーションの\ ``value``\ 属性に"hello"を指定しているので、この場合、"/echo/hello"にPOSTメソッドを使用してアクセスすると\ ``hello``\ メソッドが呼ばれる。 * - | (5) - | 引数に、EchoFormには(1)によりModelに追加されたEchoFormオブジェクトが渡される。 * - | (6) - | フォームで入力された\ ``name``\ を、Viewにそのまま渡す。 .. note:: \ ``@GetMapping``\ アノテーションもしくは\ ``@PostMapping``\ アノテーションをメソッドに指定する場合は、クライアントから送信されたデータの扱い方によって変えるのが一般的である。 * データをサーバに保存する場合(更新系の処理の場合)は、\ ``@PostMapping``\ アノテーション(POSTメソッド)。 * データをサーバに保存しない場合(参照系の処理の場合)は、\ ``@GetMapping``\ アノテーション(GETメソッド)。 エコーアプリケーションでは、 * \ ``index``\ メソッドはデータをサーバに保存しない処理なのでGETメソッド\ ``@GetMapping``\ アノテーション * \ ``hello``\ メソッドはデータを\ ``Model``\ オブジェクトに保存する処理なので\ ``@PostMapping``\ アノテーション を指定している。 | テンプレートHTMLの作成 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 最後に、入力画面と、出力画面について、ThymeleafのテンプレートHTMLを作成する。それぞれのファイルパスは、View名に合わせて、次のようになる。 入力画面 (src/main/webapp/WEB-INF/views/echo/index.html) を作成する。 .. code-block:: html :emphasize-lines: 7-8 Echo Application
.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | スタンダードダイアレクトが提供する属性を使用したとき、EclipseなどのIDEでの警告を抑止するため、ネームスペースを付与する。 * - | (2) - | Thymeleafの属性を利用し、HTMLフォームを構築している。\ ``th:object``\ 属性に、Controllerで用意したフォームオブジェクトの名前を指定する。 | また、ThymeleafのリンクURL式\ ``@{}``\ に "\ ``/``\ " から始まるパスを記述することでコンテキスト相対パスが生成され、\ ``th:action``\ 属性に指定できる。 | これらの属性の詳細については\ `Tutorial: Thymeleaf + Spring -Creating a Form- `_\ を参照されたい。 * - | (3) - | Thymeleaf + Springで提供される ``th:field`` 属性を用いて、特定のプロパティをHTML formにバインドすることができる。 | \ ``th:field``\ 属性は\ ``id``\ 属性、\ ``name``\ 属性、\ ``value``\ 属性をHTMLに出力し、\ ``id``\ 属性、\ ``name``\ 属性にはプロパティ名が出力される。 | \ ``th:field``\ 属性の詳細については、\ :doc:`アプリケーション層の実装 <../ImplementationAtEachLayer/ApplicationLayer>`\ を参照されたい。 .. note:: \ ``
``\ タグの\ ``method``\ 属性を省略した場合は、**GETメソッドが使用される。** 出力されるHTMLは、 .. code-block:: html :emphasize-lines: 7 Echo Application
となる。 | 出力画面 (src/main/webapp/WEB-INF/views/echo/hello.html) を作成する。 .. code-block:: html :emphasize-lines: 7 Echo Application

.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (4) - | Controllerから渡された"name"を出力する。\ ``th:text``\ 属性により、XSS対策を行っている。 | | これでエコーアプリケーションの実装は完了である。 | サーバーを起動し、 "http://localhost:8080/helloworld/echo"にアクセスするとフォームが表示される。 | 入力チェックの実装 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ここまでのアプリケーションでは、入力チェックを行っていない。 | Spring MVCでは、\ `Bean Validation `_\ をサポートしており、アノテーションベースな入力チェックを、簡単に実装することができる。 | 例として、エコーアプリケーションで名前の入力チェックを行う。 \ ``EchoForm``\ の\ ``name``\ フィールドに、入力チェックルールを指定するアノテーションを付与する。 .. code-block:: java :emphasize-lines: 5,6,12,13 package com.example.helloworld.app.echo; import java.io.Serializable; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; public class EchoForm implements Serializable { private static final long serialVersionUID = 1L; @NotNull // (1) @Size(min = 1, max = 5) // (2) private String name; public void setName(String name) { this.name = name; } public String getName() { return name; } } .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ``@NotNull``\ アノテーションをつけることで、HTTPリクエスト中に\ ``name``\ パラメータがあることを確認する。 * - | (2) - | \ ``@Size(min = 1, max = 5)``\ をつけることで、\ ``name``\ のサイズが、1以上5以下であることを確認する。 | 入力チェックが実行されるように修正し、入力チェックでエラーが発生した場合の処理を実装する。 .. code-block:: java :emphasize-lines: 5,6,28-31 package com.example.helloworld.app.echo; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.validation.annotation.Validated; 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.bind.annotation.RequestMapping; @Controller @RequestMapping("echo") public class EchoController { @ModelAttribute public EchoForm setUpEchoForm() { EchoForm form = new EchoForm(); return form; } @GetMapping public String index(Model model) { return "echo/index"; } @PostMapping(value = "hello") public String hello(@Validated EchoForm form, BindingResult result, Model model) { // (1) if (result.hasErrors()) { // (2) return "echo/index"; } model.addAttribute("name", form.getName()); return "echo/hello"; } } .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | コントローラー側には、Validation対象の引数に\ ``@Validated``\ アノテーションを付加し、\ ``BindingResult``\ オブジェクトを引数に追加する。 | Bean Validationによる入力チェックは、自動で行われる。結果は、\ ``BindingResult``\ オブジェクトに渡される。 * - | (2) - | \ ``hasErrors``\ メソッドを実行して、エラーがあるかどうかを確認する。入力エラーがある場合は、入力画面を表示するためのView名を返却する。 | 入力画面 (src/main/webapp/WEB-INF/views/echo/index.html) に、入力エラーのメッセージを表示するための実装を追加する。 .. code-block:: html :emphasize-lines: 10 Echo Application
.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | 入力画面には、エラーがあった場合に、エラーメッセージを表示するため、 ``th:errors`` 属性を追加する。 | | 以上で、入力チェックの実装は完了である。 | 実際に、次のような場合、エラーメッセージが表示される。 * 名前を空にして送信した場合 * 5文字より大きいサイズで送信した場合 .. figure:: images_FirstApplication/AppValidationEmpty.png :alt: Validation Error (name is empty) .. figure:: images_FirstApplication/AppValidationSizeOver.png :alt: Validation Error (name's size is over 5) 出力されるHTMLは、 .. code-block:: html :emphasize-lines: 11 Echo Application
size must be between 1 and 5
となる。 | まとめ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ この章では、 #. \ ``mvn archetype:generate``\ を利用したブランクプロジェクトの作成方法 #. Spring MVCの基本的な設定方法 #. 最も簡易な、画面遷移方法 #. 画面間での値の引き渡し方法 #. シンプルな入力チェック方法 を学んだ。 上記の内容が理解できていない場合は、もう一度、本節を読み、環境構築から始めて、進めていくことで理解が深まる。 .. raw:: latex \newpage