はじめての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 `_ 1.8 * - IDE - `Spring Tool Suite `_ 4.17.1.RELEASE (以降「STS」と呼ぶ。設定方法は :doc:`../Appendix/SpringToolSuite4` を参照されたい。) * - Build Tool - `Apache Maven `_ 3.8.6 (以降「Maven」と呼ぶ) * - Application Server - `Apache Tomcat `_ 9.0.73 * - Web Browser - `Google Chrome `_ 109.0.5414.120 .. note:: インターネット接続するために、プロキシサーバーを介する必要がある場合、 以下の作業を行うため、STSのProxy設定と、 `MavenのProxy設定 `_\ が必要である。 新規プロジェクト作成 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ インターネットから `mvn archetype:generate` を利用して、プロジェクトを作成する。 .. code-block:: console mvn archetype:generate -B^ -DarchetypeGroupId=org.terasoluna.gfw.blank^ -DarchetypeArtifactId=terasoluna-gfw-web-blank-archetype^ -DarchetypeVersion=5.7.2.RELEASE^ -DgroupId=com.example.helloworld^ -DartifactId=helloworld^ -Dversion=1.0.0-SNAPSHOT ここではWindows上にプロジェクトの元を作成する。 .. code-block:: console C:\work>mvn archetype:generate -B^ More? -DarchetypeGroupId=org.terasoluna.gfw.blank^ More? -DarchetypeArtifactId=terasoluna-gfw-web-blank-archetype^ More? -DarchetypeVersion=5.7.2.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 [org.terasoluna.gfw.blank:terasoluna-gfw-web-blank-archetype:5.7.2.RELEASE] found in catalog remote [INFO] ---------------------------------------------------------------------------- [INFO] Using following parameters for creating project from Archetype: terasoluna-gfw-web-blank-archetype:5.7.2.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/NewMVCProjectImport.png :alt: New MVC Project Import :width: 60% Root Directoryに \ ``C:\work\helloworld``\ を設定し、Projectsにhelloworldのpom.xmlが選択された状態で、 [Finish] を押下する。 .. figure:: images/NewMVCProjectCreate.png :alt: New MVC Project Import :width: 60% Package Explorerに、次のようなプロジェクトが生成される。 .. figure:: images/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, 67-71 org.springframework.web.util.NestedServletException .. 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) - JSP用の\ ``ViewResolver``\ を指定し、JSPファイルの配置場所を定義する。 | 次に、Welcomeページを表示するためのController (\ ``com.example.helloworld.app.welcome.HelloController``\ ) について、簡単に説明する。 .. code-block:: java :emphasize-lines: 17,26,36,38 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.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; /** * Handles requests for the application home page. */ @Controller // (4) public class HelloController { private static final Logger logger = LoggerFactory .getLogger(HelloController.class); /** * Simply selects the home view to render by returning its name. */ @RequestMapping(value = "/", method = {RequestMethod.GET, RequestMethod.POST}) // (5) 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); // (6) return "welcome/home"; // (7) } } .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (4) - ``@Controller`` アノテーションを付けることで、DIコンテナにより、コントローラクラスが自動で読み込まれる。前述「Spring MVCの設定ファイルの説明(2)」の設定により、component-scanの対象となっている。 * - | (5) - HTTPメソッドがGETまたはPOSTで、Resource(もしくはRequest URL)が"/"で、アクセスする際に実行される。 * - | (6) - Viewに渡したいオブジェクトを\ ``Model``\ に設定する。 * - | (7) - View名を返却する。前述「Spring MVCの設定ファイルの説明(3)」の設定により、"WEB-INF/views/welcome/home.jsp"がレンダリングされる。 | 最後に、Welcomeページを表示するためのJSP (\ ``src/main/webapp/WEB-INF/views/welcome/home.jsp``\ ) について、簡単に説明する。 .. code-block:: jsp :emphasize-lines: 11 Home

Hello world!

The time on the server is ${serverTime}.

<%-- (8) --%>
.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (8) - 前述の「Controllerの説明(6)」でModelに設定したオブジェクト(serverTime)は、HttpServletRequestに格納される。 そのため、JSPで\ ``${serverTime}``\ と記述することで、Controllerで設定した値を画面に出力することができる。 **ただし、${XXX}の記述は、XSS対象になる可能性があるので、文字列を出力する場合はHTMLエスケープする必要がある。** | サーバーを起動する ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | STSで、"helloworld"プロジェクトを右クリックして、"Run As" -> "Run On Server" -> "localhost" -> "Tomcat v9.0 Server at localhost" -> "Finish"を実行し、helloworldプロジェクトを起動する。 | ブラウザに "http://localhost:8080/helloworld/" を入力し、実行すると下記の画面が表示される。 .. figure:: images/AppHelloWorldIndex.png :alt: Hello World | .. _first-application-create-an-echo-application: エコーアプリケーションの作成 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 続いて、簡単なアプリケーションを作成する。作成するのは、次の図のようなテキストフィールドに、名前を入力すると メッセージを表示する、いわゆるエコーアプリケーションである。 .. figure:: images/AppEchoIndex.png :alt: Form of Echo Application .. figure:: images/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 = 2557725707095364445L; 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: 10,13,19,21,24-26 package com.example.helloworld.app.echo; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller @RequestMapping("echo") public class EchoController { @ModelAttribute // (1) public EchoForm setUpEchoForm() { EchoForm form = new EchoForm(); return form; } @RequestMapping // (2) public String index(Model model) { return "echo/index"; // (3) } @RequestMapping(value = "hello", method = RequestMethod.POST) // (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) - | メソッドに付加した ``@RequestMapping`` アノテーションの ``value`` 属性に、何も指定しない場合、クラスに付加した ``@RequestMapping`` のルートに、マッピングされる。この場合、"/echo"にアクセスすると、 ``index`` メソッドが呼ばれる。 | ``method`` 属性に何もしない場合は、任意のHTTPメソッドでマッピングされる。 * - | (3) - | View名で"echo/index"を返すので、ViewResolverにより、 "WEB-INF/views/echo/index.jsp"がレンダリングされる。 * - | (4) - | メソッドに付加した ``@RequestMapping`` アノテーションの\ ``value``\ 属性に"hello"を、\ ``method``\ 属性に\ ``RequestMethod.POST``\ を指定しているので、この場合、"/echo/hello"にPOSTメソッドを使用してアクセスすると ``hello`` メソッドが呼ばれる。 * - | (5) - | 引数に、EchoFormには(1)によりModelに追加されたEchoFormオブジェクトが渡される。 * - | (6) - | フォームで入力された ``name`` を、Viewにそのまま渡す。 .. note:: \ ``@RequestMapping``\ アノテーションの\ ``method``\ 属性に指定する値は、 クライアントから送信されたデータの扱い方によって変えるのが一般的である。 * データをサーバに保存する場合(更新系の処理の場合)は、POSTメソッド。 * データをサーバに保存しない場合(参照系の処理の場合)は、GETメソッド又は未指定(任意のメソッド)。 エコーアプリケーションでは、 * \ ``index``\ メソッドはデータをサーバに保存しない処理なので未指定(任意のメソッド) * \ ``hello``\ メソッドはデータを\ ``Model``\ オブジェクトに保存する処理なのでPOSTメソッド を指定している。 | JSPの作成 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 最後に、入力画面と、出力画面のJSPを作成する。それぞれのファイルパスは、View名に合わせて、次のようになる。 入力画面 (src/main/webapp/WEB-INF/views/echo/index.jsp) を作成する。 .. code-block:: jsp :emphasize-lines: 7-8 Echo Application <%-- (1) --%> Input Your Name: .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | タグライブラリを利用し、HTMLフォームを構築している。 ``modelAttribute`` 属性に、Controllerで用意したフォームオブジェクトの名前を指定する。 | タグライブラリは `Spring Framework Documentation -The Form Tag- `_\を参照されたい。 .. note:: \ ````\ タグの\ ``method``\ 属性を省略した場合は、POSTメソッドが使用される。 出力されるHTMLは、 .. code-block:: html :emphasize-lines: 7 Echo Application
となる。 | 出力画面 (src/main/webapp/WEB-INF/views/echo/hello.jsp) を作成する。 .. code-block:: jsp :emphasize-lines: 8 Echo Application

Hello <%-- (2) --%>

.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (2) - | Controllerから渡された"name"を出力する。 ``c:out`` タグにより、XSS対策を行っている。 .. note:: ここではXSS対策を標準タグの ``c:out`` で実現したが、より容易に使用できる ``f:h()`` 関数を共通ライブラリで用意している。 詳細は、 :doc:`../Security/XSS` を参照されたい。 | | これでエコーアプリケーションの実装は完了である。 | サーバーを起動し、 "http://localhost:8080/helloworld/echo"にアクセスするとフォームが表示される。 | 入力チェックの実装 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ここまでのアプリケーションでは、入力チェックを行っていない。 Spring MVCでは、 `Bean Validation `_\ をサポートしており、アノテーションベースな入力チェックを、簡単に 実装することができる。例として、エコーアプリケーションで名前の入力チェックを行う。 \ ``EchoForm``\ の\ ``name``\ フィールドに、入力チェックルールを指定するアノテーションを付与する。 .. code-block:: java :emphasize-lines: 5,6,11,12 package com.example.helloworld.app.echo; import java.io.Serializable; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; public class EchoForm implements Serializable { private static final long serialVersionUID = 2557725707095364445L; @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,27-30 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.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller @RequestMapping("echo") public class EchoController { @ModelAttribute public EchoForm setUpEchoForm() { EchoForm form = new EchoForm(); return form; } @RequestMapping public String index(Model model) { return "echo/index"; } @RequestMapping(value = "hello", method = RequestMethod.POST) 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.jsp) に、入力エラーのメッセージを表示するための実装を追加する。 .. code-block:: jsp :emphasize-lines: 10 Echo Application Input Your Name: <%-- (1) --%> .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | 入力画面には、エラーがあった場合に、エラーメッセージを表示するため、 ``form:errors`` タグを追加する。 | | 以上で、入力チェックの実装は完了である。 | 実際に、次のような場合、エラーメッセージが表示される。 * 名前を空にして送信した場合 * 5文字より大きいサイズで送信した場合 .. figure:: images/AppValidationEmpty.png :alt: Validation Error (name is empty) .. figure:: images/AppValidationSizeOver.png :alt: Validation Error (name's size is over 5) 出力されるHTMLは、 .. code-block:: html :emphasize-lines: 10 Echo Application
size must be between 1 and 5
となる。 | まとめ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ この章では、 #. \ ``mvn archetype:generate``\を利用したブランクプロジェクトの作成方法 #. Spring MVCの基本的な設定方法 #. 最も簡易な、画面遷移方法 #. 画面間での値の引き渡し方法 #. シンプルな入力チェック方法 を学んだ。 上記の内容が理解できていない場合は、もう一度、本節を読み、環境構築から始めて、進めていくことで理解が深まる。 .. raw:: latex \newpage