Spring Boot: カスタムバリデーションで複数フィールドをまとめてチェックしよう!
なんでカスタムアノテーション?
「パスワード変更で新旧パスワードが同じだったらエラーにしたい」
「確認用パスワードが一致しないときエラーにしたい」
こんなとき、標準の@NotNull
や@Size
じゃ対応できませんよね。
でも大丈夫!自分でアノテーション作れば複数のフィールドをまとめてチェックできちゃいます。
今回作るもの
パスワード変更で「現在のパスワード」と「新しいパスワード」が同じだったらエラーにする
@DifferentPasswords
アノテーションを作ります!
@DifferentPasswords // ← これで複数フィールドをチェック!
public class ChangePasswordCommand {
private String currentPassword;
private String newPassword;
}
ステップ 1: アノテーションを作る
まずはアノテーション本体を作ります。たった数行です!
@Target(ElementType.TYPE) // クラスに付けるアノテーション
@Retention(RetentionPolicy.RUNTIME) // 実行時まで残す
@Constraint(validatedBy = DifferentPasswordsValidator.class) // チェック処理はこのクラス
public @interface DifferentPasswords {
String message() default "新しいパスワードは現在のパスワードと異なる必要があります";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
ここがポイント!
-
@Target(ElementType.TYPE)
: フィールド 1 個じゃなくてクラス全体にアノテーションを付ける -
@Retention(RUNTIME)
: Spring Boot が実行時に見つけられるようにする -
@Constraint
: 実際のチェック処理を書くクラスを指定
この設定で「複数フィールドをまとめてチェック」が可能になります!
ステップ 2: 実際のチェック処理を書く
次に、実際にパスワードをチェックする処理を書きます。
public class DifferentPasswordsValidator implements ConstraintValidator<DifferentPasswords, ChangePasswordCommand>{
@Override
public boolean isValid(ChangePasswordCommand command, ConstraintValidatorContext context){
// どちらかがnullなら、とりあえずOK(@NotNullに任せる)
if(command.getCurrentPassword() == null || command.getNewPassword() == null){
return true;
}
// ここが肝心!複数フィールドを比較
return !command.getCurrentPassword().equals(command.getNewPassword());
}
}
すごいところ
-
ChangePasswordCommand
オブジェクト全体が渡されるので、好きなフィールドにアクセスし放題! -
getCurrentPassword()
とgetNewPassword()
を両方ともチェックできる - 戻り値
false
でエラー、true
で OK
これで複数フィールドの関係性をチェックする準備完了です!
ステップ 3: 使ってみる!
作ったアノテーションをクラスに付けるだけ!
@DifferentPasswords // ← ここに付ける!
public class ChangePasswordCommand {
@NotNull(message = "現在のパスワードは必須です")
private String currentPassword;
@NotNull(message = "新しいパスワードは必須です")
@Size(min = 8, max = 100, message = "新しいパスワードは8文字以上100文字以内で入力してください")
private String newPassword;
// getter, setter...
}
コントローラーでは普通に@Valid
するだけ!
@PostMapping("/change-password")
public ResponseEntity<?> changePassword(@Valid @RequestBody ChangePasswordCommand command) {
// バリデーション成功時の処理
return ResponseEntity.ok().build();
}
これだけで、リクエスト受信時に自動的に複数フィールドをチェックしてくれます!
実際の動作確認
例えば、こんな JSON が POST されたとき:
{
"currentPassword": "oldpassword123",
"newPassword": "oldpassword123" // 同じパスワード!
}
自動的にDifferentPasswordsValidator
が動いて、エラーレスポンスが返されます:
{
"error": "新しいパスワードは現在のパスワードと異なる必要があります"
}
応用例:他にもこんなチェックができる
パスワード確認チェック
@PasswordMatches
public class RegisterCommand {
private String password;
private String confirmPassword;
}
日付の前後関係チェック
@StartDateBeforeEndDate
public class EventCommand {
private LocalDate startDate;
private LocalDate endDate;
}
住所の整合性チェック
@ValidAddress
public class AddressCommand {
private String prefecture;
private String city;
private String zipCode;
}
なぜカスタムアノテーションが便利?
🎯 複数フィールドを一度にチェック
標準アノテーションは 1 フィールドずつしかチェックできないけど、カスタムならオブジェクト全体を見れる!
🔄 再利用できる
一度作れば、同じパターンのチェックは使い回し放題
📖 コードが読みやすい
@DifferentPasswords
って書いてあるだけで何をチェックしてるか一目瞭然
🛠️ Spring Boot と完全統合
@Valid
するだけで自動的に動く。特別な設定不要!
まとめ
たった 2 つのクラスを作るだけで、複数フィールドの関係性をチェックできる強力なバリデーションが作れます!
- アノテーション定義(
@Target(ElementType.TYPE)
がポイント) - バリデータクラス(オブジェクト全体にアクセス可能)
- 使いたいクラスに付けるだけ
標準アノテーションでは無理だった「フィールド同士の比較」が簡単にできちゃいます。ぜひ試してみてください!