SpringBeanValidation
データ検証は、Webアプリケーション開発の新しいトピックではありません。
一般的なJavaエコシステム、具体的にはSpringFrameworkでのデータ検証について簡単に説明します。 Javaプラットフォームは、データ検証を実装するためのデファクトスタンダードです。 つまり、BeanValidation仕様です。 BeanValidation仕様にはいくつかのバージョンがあります:
- 1.0(JSR-303)、
- 1.1(JSR-349)、
- 2.0(JSR 380)–最新バージョン
この仕様は、コンポーネント、インターフェース、および注釈のセットを定義します。これは、パラメーターに制約を設定し、メソッドの戻り値とコンストラクターのパラメーターを返す標準的な方法を提供し、オブジェクトとオブジェクトグラフを検証するためのAPIを提供します。
宣言型モデルは、オブジェクトとそのフィールドに注釈の形式で制約を設定するために使用されます。 @NotNull
のような事前定義されたアノテーションがあります 、 @Digits
、 @Pattern
、 @Email
、 @CreditCard
。新しいカスタム制約を作成する機能があります。
検証は、他の仕様やフレームワークが適切なタイミングでデータを検証する場合、手動またはより自然に実行できます。たとえば、JPAでのユーザー入力、挿入、更新などです。
Javaの例での検証
通常のJavaアプリケーション内のこの単純なBeanValidationの例で、実際にどのように実行できるかを見てみましょう。
制約付きのすべてのフィールドで検証するオブジェクトがあります。
public class SimpleDto {
@Min(value = 1, message = "Id can't be less than 1 or bigger than 999999")
@Max(999999)
private int id;
@Size(max = 100)
private String name;
@NotNull
private Boolean active;
@NotNull
private Date createdDatetime;
@Pattern(regexp = "^asc|desc$")
private String order = "asc";
@ValidCategory(categoryType="simpleDto")
private String category;
…
Constructor, getters and setters
これで、単純なJavaアプリケーションで使用して、オブジェクトを手動で検証できます。
public class SimpleApplication {
public static void main(String[] args) {
final SimpleDto simpleDto = new SimpleDto();
simpleDto.setId(-1);
simpleDto.setName("Test Name");
simpleDto.setCategory("simple");
simpleDto.setActive(true);
simpleDto.setOrder("asc");
simpleDto.setCreatedDatetime(new Date());
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.usingContext().getValidator();
Set constrains = validator.validate(simpleDto);
for (ConstraintViolation constrain : constrains) {
System.out.println(
"[" + constrain.getPropertyPath() + "][" + constrain.getMessage() + "]"
);
}
}
}
そして、コンソールの結果は次のようになります:
“[id] [Id can't be less than 1 or bigger than 999999]”
検証の制約と注釈
カスタム検証制約とアノテーションを作成します。
@Retention(RUNTIME)
@Target(FIELD)
@Constraint(validatedBy = {ValidCategoryValidator.class})
public @interface ValidCategory {
String categoryType();
String message() default "Category is not valid";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And constraint validation implementation:
public class ValidCategoryValidator implements ConstraintValidator<ValidCategory, String> {
private static final Map<String, List> availableCategories;
static {
availableCategories = new HashMap<>();
availableCategories.put("simpleDto", Arrays.asList("simple", "advanced"));
}
private String categoryType;
@Override
public void initialize(ValidCategory constraintAnnotation) {
this.setCategoryType(constraintAnnotation.categoryType());
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
List categories = ValidCategoryValidator.availableCategories.get(categoryType);
if (categories == null || categories.isEmpty()) {
return false;
}
for (String category : categories) {
if (category.equals(value)) {
return true;
}
}
return false;
}
}
上記の例では、使用可能なカテゴリは単純なハッシュマップから取得されます。実際のアプリケーションのユースケースでは、データベースまたはその他のサービスから取得できます。
制約と検証は、フィールドレベルだけでなく、オブジェクト全体に対しても指定および実行できることに注意してください。
開始日など、さまざまなフィールドの依存関係を検証する必要がある場合、終了日以降にすることはできません。
Bean Validationの最も広く使用されている実装 仕様はHibernateValidator およびApacheBVal 。
Springによる検証
Springフレームワークは、検証のためのいくつかの機能を提供します。
- Bean Validation APIバージョン1.0、1.1(JSR-303、JSR-349)のサポートは、バージョン3以降のSpringFrameworkで導入されました。
- Springには、非常に基本的な独自のValidatorインターフェースがあり、特定のDataBinderインスタンスで設定できます。これは、アノテーションなしで検証ロジックを実装するのに役立つ可能性があります。
Springを使用したBeanValidation
Spring Bootは、プロジェクトに含めることができる検証開始を提供します:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
このスターターは、 Hibernate Validatorのバージョンを提供します 現在のSpringBootと互換性があります。
Bean Validationを使用すると、パス内のリクエスト本文、クエリパラメータ、変数を検証できます(例: / / simpledto / {id} )、または任意のメソッドまたはコンストラクターパラメーター。
POSTまたはPUTリクエスト
たとえば、POSTまたはPUTリクエストでは、JSONペイロードを渡します。Springはそれを自動的にJavaオブジェクトに変換し、結果のオブジェクトを検証します。 SimpleDto
を使用しましょう 1つの例のオブジェクト:
@RestController
@RequestMapping("/simpledto")
public class SimpleDtoController {
@Autowired
private SimpleDtoService simpleDtoService;
@RequestMapping(path = "", method = RequestMethod.POST, produces =
"application/json")
public SimpleDto createSimpleDto(
@Valid @RequestBody SimpleDto simpleDto) {
SimpleDto result = simpleDtoService.save(simpleDto);
return result;
}
}
@Valid
を追加しました SimpleDto
への注釈 @RequestBody
で注釈が付けられたパラメーター 。これにより、実際のメソッド呼び出しを行う前に検証を処理するようにSpringに指示されます。検証が失敗した場合、Springは MethodArgument NotValidException
をスローします これは、デフォルトで400(Bad Request)応答を返します。
パス変数の検証
パス変数の検証は少し異なります。問題は、オブジェクトの内部ではなく、メソッドパラメータに直接制約アノテーションを追加する必要があることです。
これを機能させるには、2つの可能なオプションがあります:
オプション1:@Validated Annotation
@Validated
を追加します メソッドパラメータの制約アノテーションを評価するためのクラスレベルでのコントローラへのアノテーション。
オプション2:パス変数
次の例に示すように、パス変数を表すオブジェクトを使用します。
@RestController
@RequestMapping("/simpledto")
public class SimpleDtoController {
@Autowired
private SimpleDtoService simpleDtoService;
@RequestMapping(path = "/{simpleDtoId}", method = RequestMethod.GET, produces =
"application/json")
public SimpleDto getSimpleDto(
@Valid SimpleDtoIdParam simpleDtoIdParam) {
SimpleDto result = simpleDtoService.findById(simpleDtoIdParam.getSimpleDtoId());
if (result == null) {
throw new NotFoundException();
}
return result;
}
}
この場合、 SimpleDtoIdParam
があります simpleDtoId
を含むクラス 標準またはカスタムのBean制約アノテーションに対して検証されるフィールド。パス変数名( / {simpleDtoId} )はフィールド名と同じである必要があります(したがって、Springはこのフィールドのセッターを見つけることができます)。
private static final long serialVersionUID = -8165488655725668928L;
@Min(value = 1)
@Max(999999)
private int simpleDtoId;
public int getSimpleDtoId() {
return simpleDtoId;
}
public void setSimpleDtoId(int simpleDtoId) {
this.simpleDtoId = simpleDtoId;
}
}
リクエスト本文とは対照的 検証、パス変数 検証はConstraintViolationException
をスローします MethodArgumentNotValidException
の代わりに 。したがって、カスタム例外ハンドラを作成する必要があります。
Java Springフレームワークでは、 @Validated
を使用してサービスレベルでパラメーターを検証することもできます。 アノテーション(クラスレベル)と@Valid
(パラメータレベル)。
Spring JPAがその下でHibernateを使用していることを考えると、エンティティークラスのBeanValidationもサポートしています。以前のすべてのロジックが無効なオブジェクトを処理していたことを意味するため、多くの場合、このレベルの検証に依存することはおそらく良い考えではないことに注意してください。
春の検証インターフェース
Springは、検証バリデーター(org.springframework.validation.Validator)用の独自のインターフェースを定義します。特定のDataBinderインスタンスに設定し、アノテーションなしで検証を実装できます(非宣言型アプローチ)。
このアプローチを実装するには、次のことを行う必要があります。
- バリデーターインターフェースを実装する
- バリデーターを追加
バリデーターインターフェースの実装
バリデーターインターフェースを実装します。たとえば、 SimpleDto
を操作できます クラス:
@Component
public class SpringSimpleDtoValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return SimpleDto.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
if (errors.getErrorCount() == 0) {
SimpleDto param = (SimpleDto) target;
Date now = new Date();
if (param.getCreatedDatetime() == null) {
errors.reject("100",
"Create Date Time can't be null");
} else if (now.before(param.getCreatedDatetime())) {
errors.reject("101",
"Create Date Time can't be after current date time");
}
}
}
}
作成されたDatetime
かどうかはこちらを確認してください タイムスタンプは将来のものです。
バリデーターの追加
バリデーターの実装をDataBinder
に追加します :
@RestController
@RequestMapping("/simpledto")
public class SimpleDtoController {
@Autowired
private SimpleDtoService simpleDtoService;
@Autowired
private SpringSimpleDtoValidator springSimpleDtoValidator;
@InitBinder("simpleDto")
public void initMerchantOnlyBinder(WebDataBinder binder) {
binder.addValidators(springSimpleDtoValidator);
}
@RequestMapping(path = "", method = RequestMethod.POST, produces =
"application/json")
public SimpleDto createSimpleDto(
@Valid @RequestBody SimpleDto simpleDto) {
SimpleDto result = simpleDtoService.save(simpleDto);
return result;
}
}
これで、 SimpleDto
ができました。 制約アノテーションとカスタムSpringValidation実装を使用して検証されました。