1概要

一般に、ユーザー入力を検証する必要がある場合、Spring MVCは標準の事前定義バリデータを提供しています。

ただし、より特定の型入力を検証する必要がある場合は、独自のカスタム検証ロジックを作成する可能性があります。

この記事では、それを実行します。電話番号フィールドを含むフォームを検証するためのカスタムバリデータを作成し、次に複数のフィールドに対してカスタムバリデータを表示します。


2セットアップ

APIを利用するには、

pom.xml

ファイルに依存関係を追加します。

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>1.1.0.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.4.1.Final</version>
</dependency>

依存関係の最新バージョンはhttps://search.maven.org/classic/#search%7C1%7Cg%3A%22javax.validation%22%20AND%20a%3A%22validation-api%22[]で確認できます。ここ]とhttps://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.hibernate%22%20AND%20a%3A%22hibernate-validator%22[here]。

** 3カスタム検証

**

カスタムバリデータを作成するには、独自のアノテーションをロールアウトし、それをモデル内で使用して検証ルールを適用する必要があります。

それでは、電話番号をチェックする** カスタムバリデータを作成しましょう。

電話番号は8桁以上で11桁以下の数字でなければなりません。


4新しい注釈

注釈を定義するための新しい

@ interface

を作成しましょう。

@Documented
@Constraint(validatedBy = ContactNumberValidator.class)
@Target( { ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ContactNumberConstraint {
    String message() default "Invalid phone number";
    Class<?>[]groups() default {};
    Class<? extends Payload>[]payload() default {};
}


@ Constraint

アノテーションを使用して、フィールドを検証するクラスを定義しました。

message()

は、ユーザーインターフェイスに表示されるエラーメッセージです。追加のコードは、Spring標準に準拠するための最も定型的なコードです。

** 5バリデーターを作成する+

**

検証の規則を強制するバリデータクラスを作成しましょう。

public class ContactNumberValidator implements
  ConstraintValidator<ContactNumberConstraint, String> {

    @Override
    public void initialize(ContactNumberConstraint contactNumber) {
    }

    @Override
    public boolean isValid(String contactField,
      ConstraintValidatorContext cxt) {
        return contactField != null && contactField.matches("[0-9]+")
          && (contactField.length() > 8) && (contactField.length() < 14);
    }

}

検証クラスは

ConstraintValidator

インターフェースを実装し、

isValid

メソッドを実装する必要があります。このメソッドでは、検証規則を定義しました。

当然、バリデータがどのように機能するかを示すために、ここでは簡単な検証規則を使用します。

____ConstraintValidatorは、特定のオブジェクトに対する特定の制約を検証するためのロジックを定義します。実装は次の制限に従う必要があります。

  • オブジェクトはパラメータ化されていない型に解決されなければなりません

  • オブジェクトの総称パラメータは、無制限のワイルドカード型でなければなりません

** 6. 検証アノテーションを適用する

**

今回のケースでは、検証規則を適用するためのフィールドを1つ持つ単純なクラスを作成しました。ここでは、検証されるように注釈付きフィールドを設定しています。

@ContactNumberConstraint
private String phone;

私たちは文字列フィールドを定義し、それにカスタムアノテーション[email protected]を付けました。

@Controller
public class ValidatedPhoneController {

    @GetMapping("/validatePhone")
    public String loadFormPage(Model m) {
        m.addAttribute("validatedPhone", new ValidatedPhone());
        return "phoneHome";
    }

    @PostMapping("/addValidatePhone")
    public String submitForm(@Valid ValidatedPhone validatedPhone,
      BindingResult result, Model m) {
        if(result.hasErrors()) {
            return "phoneHome";
        }
        m.addAttribute("message", "Successfully saved phone: "
          + validatedPhone.toString());
        return "phoneHome";
    }
}

単一の

JSP

ページを持つこの単純なコントローラを定義し、

submitForm

メソッドを使用して電話番号の検証を強制します。


7. 景色

私たちの見解は、単一のフィールドを持つフォームを持つ基本的なJSPページです。ユーザーがフォームを送信すると、カスタムバリデーターによってフィールドが検証され、検証が成功したか失敗したかを示すメッセージとともに同じページにリダイレクトされます。

<form:form
  action="/${pageContext.request.contextPath}/addValidatePhone"
  modelAttribute="validatedPhone">
    <label for="phoneInput">Phone: </label>
    <form:input path="phone" id="phoneInput"/>
    <form:errors path="phone" cssClass="error"/>
    <input type="submit" value="Submit"/>
</form:form>


8テスト

それでは、コントローラをテストして、適切なレスポンスとビューが得られるかどうかを確認しましょう。

@Test
public void givenPhonePageUri__whenMockMvc__thenReturnsPhonePage(){
    this.mockMvc.
      perform(get("/validatePhone")).andExpect(view().name("phoneHome"));
}

また、ユーザー入力に基づいて、我々のフィールドが検証されていることをテストしましょう。

@Test
public void
  givenPhoneURIWithPostAndFormData__whenMockMVC__thenVerifyErrorResponse() {

    this.mockMvc.perform(MockMvcRequestBuilders.post("/addValidatePhone").
      accept(MediaType.TEXT__HTML).
      param("phoneInput", "123")).
      andExpect(model().attributeHasFieldErrorCode(
          "validatedPhone","phone","ContactNumberConstraint")).
      andExpect(view().name("phoneHome")).
      andExpect(status().isOk()).
      andDo(print());
}

テストでは、「123」という入力をユーザーに提供しています。予想どおり、すべてが機能しており、** クライアント側でエラーが発生しています。

9.カスタムクラスレベルの検証

カスタム検証アノテーションをクラスレベルで定義して、クラスの複数の属性を検証することもできます。

このシナリオの一般的な使用例は、クラスの2つのフィールドに一致する値があるかどうかを確認することです。


9.1. 注釈を作成する

後でクラスに適用できる

FieldsValueMatch

という新しいアノテーションを追加しましょう。注釈には、比較するフィールドの名前を表す2つのパラメーター

field



fieldMatch

があります。

@Constraint(validatedBy = FieldsValueMatchValidator.class)
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldsValueMatch {

    String message() default "Fields values don't match!";

    String field();

    String fieldMatch();

    @Target({ ElementType.TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    @interface List {
        FieldsValueMatch[]value();
    }
}

カスタムアノテーションには、クラスに複数の

FieldsValueMatch

アノテーションを定義するための

List

サブインターフェースも含まれています。


9.2. バリデータの作成

次に、実際の検証ロジックを含む

FieldsValueMatchValidator

クラスを追加する必要があります。

public class FieldsValueMatchValidator
  implements ConstraintValidator<FieldsValueMatch, Object> {

    private String field;
    private String fieldMatch;

    public void initialize(FieldsValueMatch constraintAnnotation) {
        this.field = constraintAnnotation.field();
        this.fieldMatch = constraintAnnotation.fieldMatch();
    }

    public boolean isValid(Object value,
      ConstraintValidatorContext context) {

        Object fieldValue = new BeanWrapperImpl(value)
          .getPropertyValue(field);
        Object fieldMatchValue = new BeanWrapperImpl(value)
          .getPropertyValue(fieldMatch);

        if (fieldValue != null) {
            return fieldValue.equals(fieldMatchValue);
        } else {
            return fieldMatchValue == null;
        }
    }
}


isValid()

メソッドは2つのフィールドの値を取得し、それらが等しいかどうかを確認します。


9.3. 注釈を付ける

2つの値を再入力するための2つの

verifyEmail

および

verifyPassword

属性とともに、2つの

email

および

password

属性を持つ、ユーザー登録に必要なデータを対象とする

NewUserForm

モデルクラスを作成しましょう。

対応するフィールドと照合するフィールドが2つあるため、

NewUserForm

クラスに

email

値用と

password

値用の2つの

@ FieldsValueMatch

アノテーションを追加します。

@FieldsValueMatch.List({
    @FieldsValueMatch(
      field = "password",
      fieldMatch = "verifyPassword",
      message = "Passwords do not match!"
    ),
    @FieldsValueMatch(
      field = "email",
      fieldMatch = "verifyEmail",
      message = "Email addresses do not match!"
    )
})
public class NewUserForm {
    private String email;
    private String verifyEmail;
    private String password;
    private String verifyPassword;

   //standard constructor, getters, setters
}

Spring MVCでモデルを検証するには、

@ Valid

というアノテーションが付いた

NewUserForm

オブジェクトを受け取り、検証エラーがないかどうかを検証する

/user

POSTマッピングを持つコントローラーを作成しましょう。

@Controller
public class NewUserController {

    @GetMapping("/user")
    public String loadFormPage(Model model) {
        model.addAttribute("newUserForm", new NewUserForm());
        return "userHome";
    }

    @PostMapping("/user")
    public String submitForm(@Valid NewUserForm newUserForm,
      BindingResult result, Model model) {
        if (result.hasErrors()) {
            return "userHome";
        }
        model.addAttribute("message", "Valid form");
        return "userHome";
    }
}


9.4. 注釈をテストする

カスタムクラスレベルのアノテーションを検証するには、一致する情報を

/user

エンドポイントに送信し、レスポンスにエラーが含まれていないことを検証する

JUnit

テストを作成します。

public class ClassValidationMvcTest {
  private MockMvc mockMvc;

    @Before
    public void setup(){
        this.mockMvc = MockMvcBuilders
          .standaloneSetup(new NewUserController()).build();
    }

    @Test
    public void givenMatchingEmailPassword__whenPostNewUserForm__thenOk()
      throws Exception {
        this.mockMvc.perform(MockMvcRequestBuilders
          .post("/user")
          .accept(MediaType.TEXT__HTML).
          .param("email", "[email protected]")
          .param("verifyEmail", "[email protected]")
          .param("password", "pass")
          .param("verifyPassword", "pass"))
          .andExpect(model().errorCount(0))
          .andExpect(status().isOk());
    }
}

次に、一致しない情報を

/user

エンドポイントに送信する

JUnit

テストを追加して、結果に2つのエラーが含まれることを表明します。

@Test
public void givenNotMatchingEmailPassword__whenPostNewUserForm__thenOk()
  throws Exception {
    this.mockMvc.perform(MockMvcRequestBuilders
      .post("/user")
      .accept(MediaType.TEXT__HTML)
      .param("email", "[email protected]")
      .param("verifyEmail", "[email protected]")
      .param("password", "pass")
      .param("verifyPassword", "passsss"))
      .andExpect(model().errorCount(2))
      .andExpect(status().isOk());
    }


10概要

このクイック記事では、フィールドまたはクラスを検証してそれらをSpring MVCに配線するためのカスタムバリデータを作成する方法を示しました。

いつものように、あなたはhttps://github.com/eugenp/tutorials/tree/master/spring-mvc-java[over on Github]からコードを見つけることができます。