Spring Controllerのリストの検証

1. 前書き

ユーザー入力の検証は、どのアプリケーションでも一般的な要件です。 このチュートリアルでは、オブジェクトの_List_をSpringコントローラーへのパラメーターとして*検証する方法について説明します。
コントローラーレイヤーに検証を追加して、ユーザー指定のデータが指定の条件を満たすことを確認します。

2. Beanへの制約の追加

この例では、映画のデータベースを管理する簡単なSpringコントローラーを使用します。 ムービーのリストを受け入れ、リストの検証を実行した後にそれらをデータベースに追加するメソッドに焦点を当てます。
そのため、https://www.baeldung.com/javax-validation [javax validation]を使用して_Movie_ Beanに制約を追加することから始めましょう。
public class Movie {

    private String id;

    @NotEmpty(message = "Movie name cannot be empty.")
    private String name;

    // standard setters and getters
}

3. コントローラーに検証アノテーションを追加する

コントローラーを見てみましょう。 最初に、コントローラークラスに_ @ Validated_アノテーションを追加します*:
@Validated
@RestController
@RequestMapping("/movies")
public class MovieController {

    @Autowired
    private MovieService movieService;

    //...
}
次に、渡された_Movie_オブジェクトのリストを検証するコントローラーメソッドを記述しましょう。
ムービーのリストに_ @ NotEmpty_注釈を追加して、リストに少なくとも1つの要素があることを検証します。 同時に、_Movie_オブジェクト自体が有効であることを確認するために、_ @ Valid_アノテーションを追加します。
@PostMapping
public void addAll(
  @RequestBody
  @NotEmpty(message = "Input movie list cannot be empty.")
  List<@Valid Movie> movies) {
    movieService.addAll(movies);
}
空の_Movie_ list入力を使用してコントローラーメソッドを呼び出すと、_ @ NotEmpty_注釈のために検証が失敗し、次のメッセージが表示されます。
Input movie list cannot be empty.
_ @ Valid_アノテーションは、_Movie_クラスで指定された制約がリスト内の各オブジェクトに対して評価されることを確認します。 したがって、リストに空の名前の_Movie_を渡すと、検証は失敗して次のメッセージが表示されます。
Movie name cannot be empty.

4. カスタム検証

入力リストにlink:/spring-mvc-custom-validator [カスタム制約バリデーター]を追加することもできます。
この例では、カスタム制約により、入力リストのサイズが最大4つの要素に制限されているという条件が検証されます。 このカスタム制約アノテーションを作成しましょう:
@Constraint(validatedBy = MaxSizeConstraintValidator.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface MaxSizeConstraint {
    String message() default "The input list cannot contain more than 4 movies.";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
次に、上記の制約を適用するバリデーターを作成します。
public class MaxSizeConstraintValidator implements ConstraintValidator<MaxSizeConstraint, List<Movie>> {
    @Override
    public boolean isValid(List<Movie> values, ConstraintValidatorContext context) {
        return values.size() <= 4
    }
}
最後に、コントローラーメソッドに_ @ MaxSizeConstraint_注釈を追加します。
@PostMapping
public void addAll(
  @RequestBody
  @NotEmpty(message = "Input movie list cannot be empty.")
  @MaxSizeConstraint
  List<@Valid Movie> movies) {
    movieService.addAll(movies);
}
ここで、_ @ MaxSizeConstraint_は入力のサイズを検証します。 したがって、入力リストで4つ以上の_Movie_オブジェクトを渡すと、検証は失敗します。

5.  例外処理

検証のいずれかが失敗すると、_https://javaee.github.io/javaee-spec/javadocs/javax/validation/ConstraintViolationException.html [ConstraintViolationException] _がスローされます。 次に、https://www.baeldung.com/exception-handling-for-rest-with-spring [例外処理]コンポーネントを追加してこの例外をキャッチする方法を見てみましょう。
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity handle(ConstraintViolationException constraintViolationException) {
    Set<ConstraintViolation<?>> violations = constraintViolationException.getConstraintViolations();
    String errorMessage = "";
    if (!violations.isEmpty()) {
        StringBuilder builder = new StringBuilder();
        violations.forEach(violation -> builder.append(" " + violation.getMessage()));
        errorMessage = builder.toString();
    } else {
        errorMessage = "ConstraintViolationException occured.";
    }
    return new ResponseEntity<>(errorMessage, HttpStatus.BAD_REQUEST);
 }

6. APIのテスト

次に、有効な入力と無効な入力を使用してコントローラーをテストします。
まず、APIに有効な入力を提供しましょう。
curl -v -d [{"name":"Movie1"}] -H "Content-Type: application/json" -X POST http://localhost:8080/movies
このシナリオでは、HTTPステータス200応答を取得します。
...
HTTP/1.1 200
...
次に、無効な入力を渡すときのAPIレスポンスをチェックします。
空のリストを試してみましょう:
curl -d [] -H "Content-Type: application/json" -X POST http://localhost:8080/movies
このシナリオでは、HTTPステータス400応答を取得します。 これは、入力が_ @ NotEmpty_制約を満たさないためです。
Input movie list cannot be empty.
次に、リスト内の5つの_Movie_オブジェクトを渡してみましょう。
curl -d [{"name":"Movie1"},{"name":"Movie2"},{"name":"Movie3"},{"name":"Movie4"},{"name":"Movie5"}]
  -H "Content-Type: application/json" -X POST http://localhost:8080/movies
_ @ MaxSizeConstraint_制約に失敗するため、これによりHTTPステータス400の応答が発生します。
The input list cannot contain more than 4 movies.

7. 結論

この簡単な記事では、Springのオブジェクトのリストを検証する方法を学びました。
いつものように、例の完全なソースコードはhttps://github.com/eugenp/tutorials/tree/master/spring-mvc-simple-2[over on GitHub]です。