Micronautで自作のBean Validationを使いたい
業務で自作のバリデーターを作る必要が出てきて、新しいフレームワークなので全然資料がない && Java初心者 で結構苦労しました。
結果的には期待する動作をするものが出来たのでメモします。
経緯
現在のタスクで、入力バリデーションとしてよくあるこのような実装が必要になりました。
- リクエストの特定のパラメータが、指定の正規表現にマッチしてるか確認
- していなければ特定のエラーメッセージを持ったエラーレスポンスを返す
素?のMicronautなら Pattern(regexp = "パターン", message="メッセージ")
で実現できると思いますが、現在のプロジェクトではアノテーション別にハンドラで処理をしており、この実装だと不都合でした。
実装
2020/03/04追記 Validatorクラスにに @Singleton
を付ける必要があり、加筆しました。
アノテーション + バリデーター
EmployeeNumber フィールドに対して、以下のいずれかを満たしているかチェックするBean Validation
- 値がNULL
- UXXXX, NXXX, KXXXX の形
package com.myproject.annotation;
import io.micronaut.core.annotation.AnnotationValue;
import io.micronaut.validation.validator.constraints.ConstraintValidator;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Singleton;
import javax.validation.Constraint;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.regex.Pattern;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target({FIELD})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {EmployeeNumberValidator.class})
public @interface EmployeeNumber {
String message() default "社員番号は社名のアルファベット頭文字 + 4桁の半角数字で入力してください。";
}
@Singleton
class EmployeeNumberValidator
implements ConstraintValidator<EmployeeNumber, String> {
private static final Pattern EmployeeNumberPattern = Pattern.compile("^[UNK]\\d{4}+$");
@Override
public boolean isValid(@Nullable String value, @Nonnull AnnotationValue<EmployeeNumber> annotationMetadata, @Nonnull io.micronaut.validation.validator.constraints.ConstraintValidatorContext context) {
if (value == null) {
return true;
}
return EmployeeNumberPattern.matcher(value).matches();
}
}
今回、 バリデーターとアノテーションをひとまとめにしましたが、別ファイルに切り出したい場合はバリデーターを public
にしてアノテーション側で import
します。
アノテーション定義インターフェース自体についてるアノテーションは、特にMicronaut固有のものでもなく検索するとすぐ出てくるので解説しません(自分もよくわかってない)
アノテーションを利用するリクエストエンティティ
package com.myproject.request;
import com.myproject.annotation.EmployeeNumber;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import io.micronaut.core.annotation.Introspected;
import lombok.Data;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
@Introspected
@Data
@JsonFormat
public class EmployeeSearchRequest {
@NotNull
private String officeCode;
private String employeeId;
@EmployeeNumber
private String employeeName;
}
つまづいたところ
当初、 javax.validation.ConstraintValidator
を実装する記事を参考に実装してみたところ、アノテーションはつけられるのに、 ConstraintViolationException
のハンドラで拾ってくれない現象が起きました。
io.micronaut.validation.validator.constraints.ConstraintValidator
を実装した バリデーターを作成をする必要があったようです。
これを アノテーションの @Constraint
に指定することで解決しました。