我有一个 Spring Data REST 项目,其实体类型具有基于实体属性的条件验证。 当该属性设置为特定值时,我想使用 validation groups 启用某些验证。
作为具体示例,采用以下实体类:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.NotNull;
@Entity
public class Animal {
public enum Type { FLYING, OTHER }
/**
* Validation group.
*/
public interface Flying {}
@Id
@GeneratedValue
private Integer id;
private Type type;
@NotNull(groups = Flying.class)
private Integer airSpeedVelocity;
@NotNull
private Integer weight;
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public Type getType() { return type; }
public void setType(Type type) { this.type = type; }
public Integer getAirSpeedVelocity() { return airSpeedVelocity; }
public void setAirSpeedVelocity(Integer airSpeedVelocity) { this.airSpeedVelocity = airSpeedVelocity; }
public Integer getWeight() { return weight; }
public void setWeight(Integer weight) { this.weight = weight;}
}
当保存类型为
Animal
的 FLYING
时,我想验证 airSpeedVelocity
是否为非空。 当拯救任何其他动物时,我不需要这种验证。
目前,我可以在保存之前检查验证,以便在对象无效时返回 400 Bad Request 错误:
@Bean
public ValidatingRepositoryEventListener preSaveValidator(
@Qualifier("defaultValidator") SmartValidator validator,
ObjectFactory<PersistentEntities> persistentEntitiesFactory) {
ValidatingRepositoryEventListener eventListener =
new ValidatingRepositoryEventListener(persistentEntitiesFactory);
eventListener.addValidator("beforeCreate", validator);
eventListener.addValidator("beforeSave", validator);
return eventListener;
}
}
要求:
{ "type": "FLYING" }
当前 400 错误响应:
{
"errors": [
{
"entity": "Animal",
"property": "weight",
"invalidValue": null,
"message": "must not be null"
}
]
}
所需的 400 错误响应:
{
"errors": [
{
"entity": "Animal",
"property": "airSpeedVelocity",
"invalidValue": null,
"message": "must not be null"
},
{
"entity": "Animal",
"property": "weight",
"invalidValue": null,
"message": "must not be null"
}
]
}
如何执行此条件验证,当请求实体是
Flying
where Animal
?时应用
type == FLYING
验证组
一种解决方案是使用自定义的
Validator
自动检查输入类型,并在必要时自动应用自定义验证组:
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.SmartValidator;
import javax.validation.groups.Default;
@Configuration
public class RestRepositoryValidatorConfig {
@Bean
public ValidatingRepositoryEventListener preSaveValidator(
AnimalValidationGroupAwareValidator validator,
ObjectFactory<PersistentEntities> persistentEntitiesFactory) {
ValidatingRepositoryEventListener eventListener =
new ValidatingRepositoryEventListener(persistentEntitiesFactory);
eventListener.addValidator("beforeCreate", validator);
eventListener.addValidator("beforeSave", validator);
return eventListener;
}
@Component
public static class AnimalValidationGroupAwareValidator
implements SmartValidator {
private final SmartValidator delegate;
public AnimalValidationGroupAwareValidator(
@Qualifier("defaultValidator") SmartValidator delegate) {
this.delegate = delegate;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
@Override
public void validate(Object target, Errors errors,
Object... validationHints) {
// If hints are overridden, use those instead
delegate.validate(target, errors, validationHints);
}
@Override
public void validate(Object target, Errors errors) {
if (target instanceof Animal animal &&
Animal.Type.FLYING.equals(animal.getType())) {
delegate.validate(target, errors,
Animal.Flying.class, Default.class);
} else {
delegate.validate(target, errors);
}
}
}
}
Default
验证组,否则也不会执行标准验证。
DefaultGroupSequenceProvider
可用于根据对象的状态动态定义默认验证组。
根据参考指南:
Hibernate Validator 还提供了一个 SPI,用于根据对象状态动态重新定义默认组序列。
为此,您需要实现接口
并通过DefaultGroupSequenceProvider
注解向目标类注册此实现。@GroupSequenceProvider
在这种情况下,可以创建一个
DefaultGroupSequenceProvider
,当对象的 Flying
属性为 type
时,它使用 FLYING
组(加上标准默认组),否则使用标准默认组。
import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider;
import java.util.List;
public class AnimalTypeGroupSequenceProvider
implements DefaultGroupSequenceProvider<Animal> {
@Override
public List<Class<?>> getValidationGroups(Animal object) {
if (object != null && object.getType() == Animal.Type.FLYING) {
return List.of(Animal.Flying.class, Animal.class);
} else {
return List.of(Animal.class);
}
}
}
import org.hibernate.validator.group.GroupSequenceProvider;
import javax.persistence.Entity;
@GroupSequenceProvider(AnimalTypeGroupSequenceProvider.class)
@Entity
public class Animal {
// ...
}