入力チェック ================================================================================ .. only:: html .. contents:: 目次 :depth: 4 Overview -------------------------------------------------------------------------------- ユーザーが入力した値が不正かどうかを検証することは必須である。 入力値の検証は大きく分けて、 #. 長さや形式など、文脈によらず入力値だけを見て、それが妥当かどうかを判定できる検証 #. システムの状態によって入力値が妥当かどうかが変わる検証 がある。 1.の例としては必須チェックや、桁数チェックがあり、2.の例としては 登録済みのE-mailかどうかのチェックや、注文数が在庫数以内であるかどうかのチェックが挙げられる。 本節では、基本的には前者のことを説明し、このチェックのことを「入力チェック」を呼ぶ。 後者のチェックは「業務ロジックチェック」と呼ぶ。業務ロジックチェックについては \ :doc:`../../ImplementationAtEachLayer/DomainLayer`\ を参照されたい。 本ガイドラインでは、基本的に入力チェックをアプリケーション層で行い、 業務ロジックチェックは、ドメイン層で行うことをポリシーとする。 Webアプリケーションの入力チェックには、サーバサイドで行うチェックと、クライアントサイド(JavaScript)で行うチェックがある。 サーバーサイドのチェックは必須であるが、クライアントサイドでも同じチェックを実施すると、 サーバー通信なしでチェック結果が分かるため、ユーザビリティが向上する。 .. warning:: JavaScriptによるクライアントサイドの処理は、改ざん可能であるため、サーバーサイドのチェックは、必ず行うこと。 クライアントサイドのみでチェックを行い、サーバーサイドでチェックを省略した場合は、システムが危険な状態に晒されていることになる。 入力チェックの分類 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 入力チェックは、単項目チェック、相関項目チェックに分類される。 .. tabularcolumns:: |p{0.15\linewidth}|p{0.30\linewidth}|p{0.25\linewidth}|p{0.30\linewidth}| .. list-table:: :header-rows: 1 :widths: 15 30 25 30 * - 種類 - 説明 - 例 - 実現方法 * - 単項目チェック - | 単一のフィールドで完結するチェック - | 入力必須チェック | 桁チェック | 型チェック - | Bean Validation (実装ライブラリとしてHibernate Validatorを使用) * - 相関項目チェック - | 複数のフィールドを比較するチェック - | パスワードと確認用パスワードの一致チェック - | `org.springframework.validation.Validator `_\ インタフェースを実装したValidationクラス | または Bean Validation Spring は、Java標準であるBean Validationをサポートしている。 単項目チェックには、このBean Validationを利用する。 相関項目チェックの場合は、Bean ValidationまたはSpringが提供している\ ``org.springframework.validation.Validator``\ インタフェースを利用する。 .. _Validation_how_to_use: How to use -------------------------------------------------------------------------------- .. _ValidationAddDependencyLibrary: 依存ライブラリの追加 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Bean Validation 2.0(Hibernate Validator 6.x)以上を使用する場合、 Bean ValidationのAPI仕様クラス(\ ``javax.validation``\ パッケージのクラス)が格納されているjarファイルとHibernate Validatorのjarファイルに加えて、 * Expression Language 3.0以上のAPI仕様クラス (\ ``javax.el``\ パッケージのクラス) * Expression Language 3.0以上のリファレンス実装クラス が格納されているライブラリが必要となる。 アプリケーションサーバにデプロイして動かす場合は、 これらのライブラリはアプリケーションサーバから提供されているため、 依存ライブラリの追加は不要である。 ただし、スタンドアロン環境(JUnitなど)で動かす場合は、これらのライブラリを依存ライブラリとして追加する必要がある。 スタンドアロン環境でBean Validation 2.0以上を動かす際に必要となるライブラリの追加例を以下に示す。 .. code-block:: xml org.apache.tomcat.embed tomcat-embed-el test .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - スタンドアロン環境で動かすプロジェクトの :file:`pom.xml` ファイルに、 Expression Language用のクラスが格納されているライブラリを追加する。 上記例では、組込み用のApache Tomcat向けに提供されているライブラリを指定している。 \ ``tomcat-embed-el``\ のjarファイルには、Expression LanguageのAPI仕様クラスとリファレンス実装クラスの両方が格納されている。 * - | (2) - JUnitを実行するために依存ライブラリが必要になる場合は、スコープは \ ``test``\ が適切である。 .. note:: 上記設定例は、依存ライブラリのバージョンを親プロジェクトである terasoluna-gfw-parent で管理する前提であるため、pom.xmlでのバージョンの指定は不要である。 .. _Validation_single_check: 単項目チェック ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 単項目チェックを実装するには、 * フォームクラスのフィールドに、Bean Validation用のアノテーションを付与する * Controllerに、検証するための\ ``@Validated``\ アノテーションを付与する * JSPに、検証エラーメッセージを表示するためのタグを追加する が必要である。 .. note:: spring-mvc.xmlに\ ````\ の設定が行われていれば、Bean Validationは有効になる。 .. _Validation_basic_validation: 基本的な単項目チェック """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 「新規ユーザー登録」処理を例に用いて、実装方法を説明する。ここでは「新規ユーザー登録」のフォームに、以下のチェックルールを設ける。 .. tabularcolumns:: |p{0.20\linewidth}|p{0.30\linewidth}|p{0.50\linewidth}| .. list-table:: :header-rows: 1 :widths: 20 30 50 * - フィールド名 - 型 - ルール * - | name - | ``java.lang.String`` - | 入力必須 | 1文字以上 | 20文字以下 * - | email - | ``java.lang.String`` - | 入力必須 | 1文字以上 | 50文字以下 | E-mail形式 * - | age - | ``java.lang.Integer`` - | 入力必須 | 1以上 | 200以下 * フォームクラス フォームクラスの各フィールドに、Bean Validationのアノテーションを付ける。 .. code-block:: java package com.example.sample.app.validation; import java.io.Serializable; import javax.validation.constraints.Email; import javax.validation.constraints.Max; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; public class UserForm implements Serializable { private static final long serialVersionUID = 1L; @NotNull // (1) @Size(min = 1, max = 20) // (2) private String name; @NotNull @Size(min = 1, max = 50) @Email // (3) private String email; @NotNull // (4) @Min(0) // (5) @Max(200) // (6) private Integer age; // omitted setter/getter } .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | 対象のフィールドが\ ``null``\ でないことを示す\ ``javax.validation.constraints.NotNull``\ を付ける。 | | Spring MVCでは、文字列の入力フィールドに未入力の状態でフォームを送信した場合、 | デフォルトではフォームオブジェクトに\ **nullではなく、空文字がバインドされる**\ 。 | この\ ``@NotNull``\ は、そもそもリクエストパラメータとして\ ``name``\ が存在することをチェックする。 * - | (2) - | 対象のフィールドの文字列長(またはコレクションのサイズ)が指定したサイズの範囲内にあることを示す\ ``javax.validation.constraints.Size``\ を付ける。 | | 上記の通り、Spring MVCではデフォルトで、未入力の文字列フィールドには、空文字がバインドされるため、 | 1文字以上というルールが入力必須を表す。 * - | (3) - | 対象のフィールドがE-mail形式であることを示す\ ``javax.validation.constraints.Email``\ を付ける。 | E-mail形式の要件が\ ``@Email`` \のチェックと合致しない場合は、\ ``javax.validation.constraints.Pattern``\を用いて、正規表現を指定する必要がある。 | \ ``@Email`` \については、\ :ref:`Validation_jsr380_doc`\を参照されたい。 * - | (4) - | 数値の入力フィールドに未入力の状態でフォームを送信した場合、フォームオブジェクトに\ ``null`` \ がバインドされるため、\ ``@NotNull``\ が\ ``age``\ の入力必須条件を表す。 * - | (5) - | 対象のフィールドが指定した数値の以上であることを示す\ ``javax.validation.constraints.Min``\ を付ける。 * - | (6) - | 対象のフィールドが指定した数値の以下であることを示す\ ``javax.validation.constraints.Max``\ を付ける。 .. tip:: Bean Validation標準のアノテーション、Hibernate Validationが用意しているアノテーションについては、\ :ref:`Validation_jsr380_doc`\ 、\ :ref:`Validation_validator_list`\ を参照されたい。 .. tip:: 入力フィールドが未入力の場合に、空文字ではなく\ ``null``\ にバインドする方法に関しては、\ :ref:`Validation_string_trimmer_editor`\ を参照されたい。 * Controllerクラス 入力チェック対象のフォームクラスに、\ ``@Validated``\ を付ける。 .. code-block:: java package com.example.sample.app.validation; import org.springframework.stereotype.Controller; 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("user") public class UserController { @ModelAttribute public UserForm setupForm() { return new UserForm(); } @RequestMapping(value = "create", method = RequestMethod.GET, params = "form") public String createForm() { return "user/createForm"; // (1) } @RequestMapping(value = "create", method = RequestMethod.POST, params = "confirm") public String createConfirm(@Validated /* (2) */ UserForm form, BindingResult /* (3) */ result) { if (result.hasErrors()) { // (4) return "user/createForm"; } return "user/createConfirm"; } @RequestMapping(value = "create", method = RequestMethod.POST) public String create(@Validated UserForm form, BindingResult result) { // (5) if (result.hasErrors()) { return "user/createForm"; } // omitted business logic return "redirect:/user/create?complete"; } @RequestMapping(value = "create", method = RequestMethod.GET, params = "complete") public String createComplete() { return "user/createComplete"; } } .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | 「新規ユーザー登録」フォーム画面を表示する。 * - | (2) - | フォームにつけたアノテーションで入力チェックをするために、フォームの引数に \ ``org.springframework.validation.annotation.Validated``\ を付ける。 * - | (3) - | (2)のチェック結果を格納する\ ``org.springframework.validation.BindingResult``\ を、引数に加える。 | この\ ``BindingResult``\ は、フォームの直後に記述する必要がある。 | | 直後に指定されていない場合は、検証後に結果をバインドできず、\ ``org.springframework.validation.BindException``\ がスローされる。 * - | (4) - | (2)のチェック結果は、\ ``BindingResult.hasErrors()``\ メソッドで判定できる。 | \ ``hasErrors()``\ の結果が\ ``true``\ の場合は、入力値に問題があるため、フォーム表示画面に戻す。 * - | (5) - | 入力内容確認画面から新規作成処理にリクエストを送る際にも、\ **入力チェックを必ず再実行すること**\ 。 | 途中でデータを改ざんすることは可能であるため、必ず業務処理の直前で入力チェックは必要である。 .. note:: \ ``@Validated``\ は、Bean Validation標準ではなく、Springの独自アノテーションである。 Bean Validation標準の\ ``javax.validation.Valid``\ アノテーションも使用できるが、\ ``@Validated``\ は\ ``@Valid``\ に比べて、 バリデーションのグループを指定できる点で優れているため、本ガイドラインではControllerの引数には、\ ``@Validated``\ を使用することを推奨する。 .. _Validation_jsp_impl_sample: * JSP \ ````\ タグで、入力エラーがある場合にエラーメッセージを表示できる。 .. code-block:: jsp <%-- WEB-INF/views/user/createForm.jsp --%> Name: <%--(1) --%>
Email:
Age:
Confirm
.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | \ ````\ タグの\ ``path``\ 属性に、対象のフィールド名を指定する。 | この例では、フィールド毎に入力フィールドの横にエラーメッセージを表示する。 フォームは、以下のように表示される。 .. figure:: ./images_Validation/validations-first-sample1.png :width: 60% このフォームに対して、すべての入力フィールドを未入力のまま送信すると、以下のようにエラーメッセージが表示される。 .. figure:: ./images_Validation/validations-first-sample2.png :width: 60% NameとEmailが空文字であることに対するエラーメッセージと、Ageが\ ``null``\ であることに対するエラーメッセージが表示されている。 .. note:: Bean Validationでは、通常、入力値が\ ``null``\ の場合は正常な値とみなす。ただし、 以下のアノテーションを除く。 * ``javax.validation.constraints.NotNull`` * ``javax.validation.constraints.NotEmpty`` * ``javax.validation.constraints.NotBlank`` 上記の例では、Ageの値は\ ``null``\ であるため、\ ``@Min``\ と\ ``@Max``\ によるチェックは正常とみなされ、 エラーメッセージは出力されていない。 次に、フィールドに何らかの値を入力してフォームを送信する。 .. figure:: ./images_Validation/validations-first-sample3.png :width: 60% | Nameの入力値は、チェック条件を満たすため、エラーメッセージが表示されない。 | E-mailの入力値は文字列長に関する条件は満たすが、E-mail形式ではないため、エラーメッセージが表示される。 | Ageの入力値は最大値を超えているため、エラーメッセージが表示される。 エラー時にスタイルを変更したい場合は、前述のフォームを、以下のように変更する。 .. code-block:: jsp Name:<%-- (1) --%> <%-- (2) --%> <%-- (3) --%>
Email:
Age:
Confirm
.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | エラー時に\ ``