カスタムバリデーターを作ってより厳密にチェックをする

## `@Email`アノテーションとは

Springフレームワークの`@Email`アノテーションは、メールアドレスの妥当性を検証するための一般的な手段です。


## `@Email`アノテーションの問題

`@Email`アノテーションは、メールアドレスのフォーマットをチェックしますが、特殊文字や全角スペースのチェックは行いません。
そのため、ユーザーが不正な文字を含むメールアドレスを入力してもエラーが発生せず、登録が完了してしまうケースがあります。
この問題に対処するためにカスタムバリデーターを実装する方法を解説します。

## カスタムバリデーターによる解決策

この問題を解決するために、カスタムアノテーションとカスタムバリデーターを実装し、全角スペースを削除してからバリデーションを行う仕組みを導入します。

カスタムアノテーションの実装は次の通りです。

```java
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CustomEmailValidator.class)
public @interface ValidEmail {
    String message() default "無効なメールアドレスです";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
```

カスタムバリデーターの実装は次の通りです。

```java
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.constraints.Email;

public class CustomEmailValidator implements ConstraintValidator<ValidEmail, String> {

    private final EmailValidator standardEmailValidator = new EmailValidator();

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null || value.isEmpty()) {
            return false;
        }

        // 全角スペースが含まれているかをチェック
        if (value.contains("　")) {
            return false;
        }
        
        // 標準のEmailアノテーションを使ったバリデーションを実行
        return standardEmailValidator.isValid(value, context);
    }
}

```

## 使用例

このカスタムアノテーションを実際のコードでどのように使用するかを見ていきます。
以下の例では、`UserRegistrationRequest`というDTOクラスで`@ValidEmail`を使用しています。

```java
public class UserRegistrationRequest {

    @ValidEmail
    private String email;

    // 他のフィールド
    private String username;
    private String password;

    // コンストラクタ、ゲッター、セッター
}
```

また、コントローラーでは、`@Valid`アノテーションと組み合わせて、リクエストボディのバリデーションを行います。

```java
@RestController
@RequestMapping("/api/users")
public class UserController {

    @PostMapping("/register")
    public ResponseEntity<String> registerUser(@RequestBody @Valid UserRegistrationRequest request) {
        // ユーザー登録のロジック
        return ResponseEntity.ok("ユーザーが正常に登録されました");
    }
}
```

この実装により、全角スペースが含まれる不正なメールアドレスが登録リクエストで送信された場合、バリデーションエラーが発生し、適切なエラーメッセージがユーザーに返されます。

## Checkstyleによる標準アノテーションの使用禁止

プロジェクト全体で誤って標準の`@Email`アノテーションが使用されないように、Checkstyleを使用してその利用を禁止します。以下はCheckstyleの設定例です。

```xml
<module name="IllegalType">
    <property name="illegalClassNames" value="javax.validation.constraints.Email"/>
    <message key="illegal.type" value="`@Email`の使用は禁止されています。代わりに`@ValidEmail`を使用してください。"/>
</module>
```

この設定により、`@Email`アノテーションが使用されると、ビルド時にエラーが発生し、`@ValidEmail`アノテーションの使用が促されます。

## まとめ

今回、`@Email`アノテーションで全角スペースが通ってしまう問題を解決するために、カスタムバリデーターを作成する方法を紹介しました。
カスタムバリデーションを使うことで、Springの標準バリデーションでは対応できないような特定の要件に柔軟に対応できることがわかりました。

さらに、カスタムバリデーターは、メールアドレスの検証だけでなく、他のさまざまなバリデーションにも応用が可能です。例えば、特定のパターンに従う文字列のチェックや、複数のフィールドにまたがるバリデーションロジックなど、プロジェクトの要件に合わせた検証を簡単に実装することができます。

カスタムバリデーションを導入することで、より厳密なデータの整合性チェックが可能になり、アプリケーションの信頼性やセキュリティを強化することができるでしょう。