Spring Boot独自バリデータ入門: APIリクエスト文字列の禁止文字チェックとアノテーション実装【Gradle対応】

はじめに

Web API では、クライアントから送信される文字列の内容が重要な意味を持ちます。例えば SQL インジェクションやクロスサイトスクリプティング(XSS)攻撃を防ぐには、入力値に不正な文字が含まれていないか検証する必要があります。Spring Boot には @NotNull@Email など多くの標準バリデータが用意されていますが、特定のビジネス要件を満たすには自分でバリデータを実装する必要があります。

独自バリデータの概要

  • アノテーションインターフェース – 検証対象のフィールドやパラメータに付与するための定義で、デフォルトメッセージや属性を持ちます。

この構造により、バリデーションロジックを再利用できるきれいな API として公開できます。

依存関係の追加(Gradle)

Gradle で Spring Boot のバリデーションを利用するには、spring-boot-starter-validation を依存関係に追加します。build.gradle に以下を追加してください。

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-validation'
    // 他の依存関係
}

このスターターには Jakarta Bean Validation (Hibernate Validator) が含まれているため、独自バリデータを使用する準備が整います。

アノテーションインターフェースの作成

まず、禁止文字チェック用のアノテーション NoForbiddenChars を定義します。アノテーションには以下の要素を定義します。

  • message … バリデーション失敗時に返すメッセージ。
  • forbiddenChars … 禁止したい文字列を指定できるようにする独自属性。
package com.example.validation;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * 文字列に禁止文字が含まれていないことを検証するアノテーション。
 */
@Documented
@Constraint(validatedBy = NoForbiddenCharsValidator.class)
@Target({FIELD, PARAMETER})
@Retention(RUNTIME)
public @interface NoForbiddenChars {
    String message() default "不正な文字が含まれています";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};

    /**
     * 禁止する文字列。デフォルトでは OS 依存の制御文字と絵文字を禁止。
     */
    String forbiddenChars() default "\u0000-\u001f\u007f";
}

アノテーションのポイント

  • @Documented を付与して Javadoc に反映させます。
  • @Target でフィールドとメソッドパラメータに適用できるよう指定しています。

バリデータクラスの実装

package com.example.validation;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

/**
 * NoForbiddenChars アノテーションのバリデータ。
 */
public class NoForbiddenCharsValidator implements ConstraintValidator<NoForbiddenChars, String> {
    private String forbiddenRegex;

    @Override
    public void initialize(NoForbiddenChars annotation) {
        // アノテーションから禁止文字を取得し、正規表現に変換
        String chars = annotation.forbiddenChars();
        // 例えば "abc" -> [abc] のように文字クラスに変換
        this.forbiddenRegex = "[" + chars + "]";
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        // null や空文字の場合は他のアノテーション (@NotBlank など) に任せる
        if (value == null || value.isEmpty()) {
            return true;
        }
        // 禁止文字に一致する箇所が無ければ true
        return !value.matches(".*" + forbiddenRegex + ".*");
    }
}

実装のポイント

  • initialize メソッドでアノテーションの属性から禁止文字の正規表現を生成します。
  • isValid では値が null または空文字の場合は true を返し、@NotBlank など他の制約に任せます。Bean Validation API は複数のアノテーションを組み合わせて使えるので、責務を分担するのがよいでしょう。
  • 禁止文字の指定は正規表現文字クラスとして扱えるよう、角括弧で囲んでいます。

DTO やエンドポイントでの利用

作成したアノテーションは、コントローラのリクエスト DTO やメソッド引数に付与するだけで利用できます。

package com.example.api.dto;

import com.example.validation.NoForbiddenChars;
import jakarta.validation.constraints.NotBlank;

public class UserCreateRequest {
    @NotBlank(message = "ユーザー名は必須です")
    @NoForbiddenChars(forbiddenChars = "\\u0000-\\u001f@#$%")
    private String username;

    // 他のフィールドと getter/setter 省略
}

コントローラでは @Valid を付けて DTO を引数に取れば、リクエストを受け取る際にバリデーションが実行されます。

package com.example.api.controller;

import com.example.api.dto.UserCreateRequest;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Validated
public class UserController {
    @PostMapping("/users")
    public String createUser(@RequestBody @Validated UserCreateRequest request) {
        // バリデーションに成功した場合のみここに到達
        return "ユーザー " + request.getUsername() + " を作成しました";
    }
}

バリデーションエラーが発生すると、Spring Boot は自動的に MethodArgumentNotValidException を投げます。グローバル例外ハンドラを用意してエラーメッセージを整形すると、API ユーザーにとってわかりやすいレスポンスになります。

フォビドゥン文字のカスタマイズ

forbiddenChars 属性はデフォルトで制御コードを禁止していますが、アノテーションを使用する際に任意の値を指定できます。例えば絵文字や @, # といった記号を禁止したい場合は次のようにします。

@NoForbiddenChars(forbiddenChars = "@#$%😆😁")
private String comment;

このように必要に応じて禁止文字を追加できるため、複数のフィールドに対して共通のロジックを再利用しつつ柔軟に設定できます。

まとめ

この記事では禁止文字チェックの例として、Gradle プロジェクトに依存関係を追加し、アノテーション NoForbiddenChars とバリデータ NoForbiddenCharsValidator を実装しました。アノテーションの属性として禁止文字をカスタマイズできるようにしたことで、多様な要件に対応できます。これを応用すれば、電話番号の書式チェックや日付の前後関係など複雑な検証も実現できるでしょう。

是非フォローしてください

最新の情報をお伝えします

類似投稿