🐷

Spring Bootのリクエストのバリデーションとアノテーションについて

に公開

はじめに

こんにちは、クラウドエースの許です。
Spring Boot には、リクエストパラメータが正しいかを検証し、パラメータ不正時にリクエストを弾くことができる機能が備わっています。
そして、この機能はリクエストパラメータにアノテーションを付与することで実現されます。

例えば、以下のように @NotNull を付与することで、リクエストパラメータが null でないことを検証することができます。

    @NotNull
    private String string_value;

そして、これらのアノテーションは、jakarta.validation.constraints にて定義されています。
今回はそれら全てのアノテーション(xxx.List については省略)とその指定方法について解説していきます。

アノテーション一覧早見表

アノテーション名 ドキュメント説明 意訳
@AssertFalse The annotated element must be false. 値が false であることを検証
@AssertTrue The annotated element must be true. 値が true であることを検証
@DecimalMax The annotated element must be a number whose value must be less than or equal to the specified maximum. 値が指定された最大値以下であることを検証
@DecimalMin The annotated element must be a number whose value must be greater than or equal to the specified minimum. 値が指定された最小値以上であることを検証
@Digits The annotated element must be a number within the specified range. 値(小数)が指定した桁数内であることを検証
@Email The annotated element must be a valid email address. 値が有効なメールアドレスであることを検証
@Future The annotated element must be a date in the future. 値が未来の日付であることを検証
@FutureOrPresent The annotated element must be a date in the future or present. 値が未来または現在の日付であることを検証
@Max The annotated element must be a number whose value must be less than or equal to the specified maximum. 値が指定された最大値以下であることを検証
@Min The annotated element must be a number whose value must be greater than or equal to the specified minimum. 値が指定された最小値以上であることを検証
@Negative The annotated element must be a negative number. 値が負の数であることを検証
@NegativeOrZero The annotated element must be a negative number or zero. 値が負の数またはゼロであることを検証
@NotBlank The annotated element must not be null and must contain at least one non-whitespace character. 値が null でなく、かつ非空白文字を含むことを検証
@NotEmpty The annotated element must not be null and must not be empty. 値が null でなく、かつ空でないことを検証
@NotNull The annotated element must not be null. 値が null でないことを検証
@Null The annotated element must be null. 値が null であることを検証
@Past The annotated element must be a date in the past. 値が過去の日付であることを検証
@PastOrPresent The annotated element must be a date in the past or present. 値が過去または現在の日付であることを検証
@Pattern The annotated element must match the specified regular expression. 値が指定された正規表現に一致することを検証
@Positive The annotated element must be a positive number. 値が正の数であることを検証
@PositiveOrZero The annotated element must be a positive number or zero. 値が正の数またはゼロであることを検証
@Size The annotated element must be a string whose size must be within the specified boundaries. 文字列の長さが指定された範囲内であることを検証

参考:jakarta.validation.constraints

動作検証方法

それぞれの指定方法について解説する前に、動作検証を行うための環境構築方法について説明します。

  1. Spring Initializr にアクセスします。
  2. 以下のように設定します。
    • Project: Maven
    • Language: Java
    • Spring Boot: 3.4.5
    • Project Metadata
      • Group: com.example
      • Artifact: demo
      • Name: demo
      • Package name: com.example.demo
      • Packaging: Jar
      • Java: 21
    • Dependencies
      • Spring Web
      • Lombok
      • Validation
        ※ Validation を追加しないと、それぞれのアノテーションは利用できません。
  3. [Generate] をクリックし、ダウンロードした zip ファイルを解凍します。
  4. 解凍した内容を IDE 等で開きます。
  5. src/main/java/com/example/demodto パッケージを作成し、以下のように DemoDTO.java を作成します。
    DTO クラスはリクエストパラメータやレスポンスパラメータを定義するクラスです。
    これを作成することで、リクエストパラメータの管理が容易になります。
    package com.example.demo.dto;
    
    import lombok.Data;
    
    import java.time.LocalDateTime;
    
    @Data
    public class DemoDTO {
    
        private String string_value;
        private Integer integer_value;
        private Float float_value;
        private LocalDateTime date_time_value;
        private Boolean boolean_value;
        private String email;
    }
    
  6. src/main/java/com/example/democontroller パッケージを作成し、以下のように DemoController.java を作成します。
    Controller クラスはリクエストを受け取り、レスポンスを返すクラスです。
    ここでは、単純にリクエストを受け取り、リクエストパラメータをそのままレスポンスとして返す API を作成します。
    package com.example.demo.controller;
    
    import com.example.demo.dto.DemoDTO;
    import jakarta.validation.Valid;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class DemoController {
    
        @PostMapping("/demo")
        public DemoDTO demo(
                @RequestBody
                @Valid
                DemoDTO demoDTO
        ) {
            return demoDTO;
        }
    }
    

これで以下のような形式でリクエストボディをPOSTで送信する準備が完了しました。(送信先エンドポイントは http://127.0.0.1:8080/demo)
アノテーションは付与していない状態なので、バリデーションは行われません。

{
    "string_value": "string",
    "integer_value": 1,
    "float_value": 1.0,
    "date_time_value": "2023-10-01T00:00:00",
    "boolean_value": true,
    "email": "test@email.com"
}

DemoDTO を利用してアノテーションを付与する方法

実際にバリデーションを行うために、DemoDTO クラスにアノテーションを付与していきます。
基本的には、以下のように、リクエストパラメータのフィールドにアノテーションを付与するだけでバリデーションが行われます。

    public class DemoDTO {

        @NotNull
        private String string_value;
        @Max(value = 100)
        private Integer integer_value;
        private Float float_value;
        private LocalDateTime date_time_value;
        private Boolean boolean_value;
        @Email
        private String email;
    }

ここのアノテーションの指定方法は、後述するアノテーションの機能解説を参考にしてください。

バリデーションエラー時のレスポンスをカスタマイズする

作成した API がバリデーションエラーになると、以下のようなレスポンスになります。

{
    "timestamp": "2025-05-08T00:31:29.102+00:00",
    "status": 400,
    "error": "Bad Request",
    "path": "/demo"
}

ただし、この状態だと何がバリデーションに引っかかったのかレスポンスでは判別できないため、APIを公開する場合など、API利用側で何のパラメータが間違っているのか判別できません。
そのため、レスポンスをカスタマイズして、どのリクエストが、どのように間違っているかを確認できるようにしていきます。

やり方はとても簡単で、メソッドを一つ追加するだけです。

src/main/java/com/example/demo/exception/GlobalExceptionHandler.java を作成し、以下のように記載します。
GlobalExceptionHandler クラスは、Exception をキャッチしてレスポンスをカスタマイズしてくれるクラスです。
Spring Boot では、@RestControllerAdvice アノテーションを付与することで、Controller クラスで発生した例外をキャッチしてくれます。

package com.example.demo.exception;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;
import java.util.Map;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, String>> handleValidationExceptions(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getFieldErrors().forEach(error -> {
            errors.put(error.getField(), error.getDefaultMessage());
        });
        return ResponseEntity.badRequest().body(errors);
    }

}

この状態で、APIにリクエストを送ると以下のようなレスポンスが取得できます。

{
    "string_value": "空白は許可されていません"
}

これで、どのリクエストが、どのように間違っているかを確認できるようになったかと思います。

アノテーションの機能解説

前段が長くなってしまいましたが、ここからはそれぞれのアノテーションの機能について解説していきます。

@AssertFalse

リクエストパラメータが false であることを検証します。

    @AssertFalse
    private Boolean boolean_value;

@AssertTrue

リクエストパラメータが true であることを検証します。

    @AssertTrue
    private Boolean boolean_value;

@DecimalMax

リクエストパラメータが指定された最大値以下であることを検証します。
※value は string 型で指定する必要があります。

    @DecimalMax(value = "100.0")
    private Float float_value;

@DecimalMin

リクエストパラメータが指定された最小値以上であることを検証します。
※value は string 型で指定する必要があります。

    @DecimalMin(value = "0.0")
    private Float float_value;

@Digits

リクエストパラメータが指定された桁数の範囲内であることを検証します。
integer は整数部の桁数、fraction は小数部の桁数を指定します。
例は、整数部が3桁、小数部が2桁の範囲内であることを検証します。

    @Digits(integer = 3, fraction = 2)
    private Float float_value;

@Email

リクエストパラメータが有効なメールアドレスであることを検証します。
メールアドレスの形式は xxxxx@domain.name のような形式です。
検証した結果、 @ の前後に一字以上の文字があれば、チェックは通過する仕様になります。(x@x のような形式)

    @Email
    private String email;

@Future

リクエストパラメータが未来の日付であることを検証します。
ただし、現在の日時データの場合はチェックに引っかかるので、現在も対象にしたい場合は @FutureOrPresent を使用してください。

    @Future
    private LocalDateTime date_time_value;

@FutureOrPresent

リクエストパラメータが未来または現在の日付であることを検証します。

    @FutureOrPresent
    private LocalDateTime date_time_value;

@Max

リクエストパラメータが指定された最大値以下であることを検証します。
※整数型のみが対象のため、小数点型を対象にしたい場合は @DecimalMax を使用してください。

    @Max(value = 100)
    private Integer integer_value;

@Min

リクエストパラメータが指定された最小値以上であることを検証します。
※整数型のみが対象のため、小数点型を対象にしたい場合は @DecimalMin を使用してください。

    @Min(value = 0)
    private Integer integer_value;

@Negative

リクエストパラメータが負の数であることを検証します。
ただし、0 の場合はバリデーションに引っかかります。

    @Negative
    private Integer integer_value;

@NegativeOrZero

リクエストパラメータが負の数またはゼロであることを検証します。

    @NegativeOrZero
    private Integer integer_value;

@NotBlank

リクエストパラメータが null でなく、かつ空白文字(タブ文字や改行コードなどを含む)の文字を含むことを検証します。

    @NotBlank
    private String string_value;

@NotEmpty

リクエストパラメータが null でなく、かつ空でないことを検証します。
@NotBlank と異なり、こちらは空白文字が含まれている場合は通過します。

    @NotEmpty
    private String string_value;

@NotNull

リクエストパラメータが null でないことを検証します。
"string_value": ""のようなリクエストは通過しますが、"string_value": null のようなリクエストは通過しません。

    @NotNull
    private String string_value;

@Null

リクエストパラメータが null であることを検証します。

    @Null
    private String string_value;

@Past

リクエストパラメータが過去の日付であることを検証します。
ただし、現在の日時データの場合はチェックに引っかかるので、現在も対象にしたい場合は @PastOrPresent を使用してください。

    @Past
    private LocalDateTime date_time_value;

@PastOrPresent

リクエストパラメータが過去または現在の日付であることを検証します。

    @PastOrPresent
    private LocalDateTime date_time_value;

@Pattern

リクエストパラメータが指定された正規表現に一致することを検証します。

    @Pattern(regexp = "^[a-zA-Z0-9]+$")
    private String string_value;

@Positive

リクエストパラメータが正の数であることを検証します。
ただし、0 の場合はバリデーションに引っかかります。

    @Positive
    private Integer integer_value;

@PositiveOrZero

リクエストパラメータが正の数またはゼロであることを検証します。

    @PositiveOrZero
    private Integer integer_value;

@Size

リクエストパラメータの文字数が指定された範囲内であることを検証します。
受け取る文字数を指定したい場合に有効です。

    @Size(min = 1, max = 3)
    private String string_value;

まとめ

以上、Spring Boot のバリデーションアノテーションについて解説しました。
バリデーションの実装の際に、こちらの記事を参考に記載いただければと思います。

Discussion