如何在 Spring Data REST 中有条件地应用验证组?

问题描述 投票:0回答:2

我有一个 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

验证组
java spring spring-boot spring-data-rest bean-validation
2个回答
0
投票

一种解决方案是使用自定义的

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
验证组,否则也不会执行标准验证。


0
投票

Hibernate Validation

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 {
   // ...
}
© www.soinside.com 2019 - 2024. All rights reserved.