前回、Controllerでユーザーからのリクエストデータ(@RequestBody や @RequestParam)を受け取る方法を学びました。
しかし、送られてくるデータは信用できません。空欄だったり、文字数が多すぎたり、不正な形式だったりする可能性があります。これらの不正な入力を防ぎ、システムを安定稼働させるために不可欠なのが「バリデーション(Validation:入力値検証)」と「エラーハンドリング」です。
今回は、Spring Bootで標準的に使われるBean Validationの仕組みと、エラーが発生した際の適切な処理方法について学びます。
1. Bean Validationの基本:制約アノテーション
Javaには、データの検証ルールを定義するための標準仕様(Bean Validation)があります。Spring Bootはこれをサポートしており、DTO(データ転送オブジェクト)のフィールドに制約アノテーションを付けるだけで、検証ルールを設定できます。
1-1. DTOと制約アノテーションの定義
前回使用した LoginForm DTOに、検証ルールを追加してみましょう。
Java
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
@Data
public class LoginForm {
// @NotBlank: null、空文字列、空白文字のみで構成された文字列を許可しない
@NotBlank(message = "ユーザー名は必須です")
@Size(max = 20, message = "ユーザー名は20文字以内にしてください")
private String username;
// @Size: 文字列の長さを8文字以上30文字以下に制限
@Size(min = 8, max = 30, message = "パスワードは8文字以上30文字以下で入力してください")
private String password;
// @Email: メールアドレス形式であることを要求
@Email(message = "有効なメールアドレス形式ではありません")
private String email;
}
| 主な制約アノテーション | 意味 |
| @NotNull | 値が null でないこと。 |
| @NotEmpty | 文字列やコレクションが空でないこと。 |
| @NotBlank | 文字列が null でなく、空白でもないこと。 |
| @Size | 文字列やコレクションのサイズを指定範囲内にすること。 |
| @Min / @Max | 数値の最小値/最大値を指定すること。 |
| @Pattern | 正規表現に一致すること。 |
2. Controllerでの検証実行:@Valid
DTOに制約アノテーションを定義しただけでは、検証は実行されません。Controllerのメソッドで、検証をトリガーする必要があります。
Controllerのメソッドの引数としてDTOを受け取る際に、@Valid アノテーションを付けます。
Java
import jakarta.validation.Valid;
// ... その他のimport
@RestController
@RequestMapping("/auth")
public class AuthController {
// ① @Valid を付けることで、LoginFormの制約をチェックする
@PostMapping("/login")
public String login(@RequestBody @Valid LoginForm form) {
// 検証エラーがなければ、このコードが実行される
return "ログイン処理を実行します: " + form.getUsername();
}
}
2-1. 検証エラーが発生した場合
上記のコードで LoginForm の検証エラーが発生した場合、Springは自動的に MethodArgumentNotValidException という例外を発生させます。
この例外を適切に処理しないと、ユーザーには**「HTTP 500 Internal Server Error」**といった不親切なエラーメッセージが表示されてしまいます。
3. エラーハンドリングの標準的な方法
業務システムでは、エラー発生時にユーザーにわかりやすいメッセージを返したり、ログを記録したりする必要があります。Springでは、@ExceptionHandler アノテーションを使った集中管理が標準です。
3-1. @ExceptionHandler の利用
@RestControllerAdvice が付いたクラス(または @ControllerAdvice)を作成し、アプリケーション全体で発生した例外を一元的に捕捉して処理します。
Java
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.http.HttpStatus;
import java.util.Map;
import java.util.stream.Collectors;
// アプリケーション全体のエラーを捕捉する設定クラス
@RestControllerAdvice
public class GlobalExceptionHandler {
// MethodArgumentNotValidException が発生した場合にこのメソッドを呼び出す
@ExceptionHandler(MethodArgumentNotValidException.class)
// レスポンスのHTTPステータスコードを 400 Bad Request に設定
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> handleValidationExceptions(MethodArgumentNotValidException ex) {
// 発生したエラーメッセージを収集し、Map形式で返す
Map<String, String> errors = ex.getBindingResult().getFieldErrors().stream()
.collect(Collectors.toMap(
error -> error.getField(), // エラーが発生したフィールド名
error -> error.getDefaultMessage() // 定義したエラーメッセージ
));
return errors; // 例: {"username": "ユーザー名は必須です", "password": "..."}
}
}
この処理により、不正なリクエストがあった場合、サーバーは以下のようなJSON形式のエラーレスポンスを返します。
【レスポンス例】
JSON
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"username": "ユーザー名は必須です",
"password": "パスワードは8文字以上30文字以下で入力してください"
}
これにより、クライアント側(フロントエンド)は、HTTP 400 ステータスと具体的なエラーメッセージを受け取り、ユーザーに親切なフィードバックを表示できるようになります。
✅ 本日のまとめ
- バリデーションは、システムを不正な入力から守るために必須の処理である。
- Bean Validation の制約アノテーション(@NotBlank、@Size など)をDTOに付けることで検証ルールを定義する。
- Controllerの引数に @Valid を付けることで、検証をトリガーする。
- @RestControllerAdvice クラスと @ExceptionHandler メソッドを使い、MethodArgumentNotValidException を捕捉して、統一されたエラーレスポンス(HTTP 400 など)を返す。
🔔 次回予告
これで、ユーザーからのリクエストを受け付け、検証するまでの一連の流れをマスターしました。次は、Webアプリケーションの最も重要な部分である「業務ロジック」と「データアクセス」に進みます。
次回は、Controllerから処理を委譲されるService層の役割と、**DI(依存性の注入)**を使ってController、Service、Repositoryの三層構造を連携させる具体的な方法を学びます。
次回:【第18回】三層構造の連携とService層の役割 にご期待ください!


コメント