Подтвердить что ты не робот

JSR 303 Validation, если одно поле равно "что-то", то эти другие поля не должны быть нулевыми

Я хочу сделать небольшую пользовательскую проверку с помощью JSR-303 javax.validation.

У меня есть поле. И если в это поле введено определенное значение, я хочу потребовать, чтобы несколько других полей не были null.

Я пытаюсь понять это. Не уверен, что бы я назвал это, чтобы помочь найти объяснение.

Любая помощь будет оценена по достоинству. Я новичок в этом.

В настоящий момент я думаю о пользовательских ограничениях. Но я не уверен, как проверить значение зависимого поля из аннотации. В принципе, я не уверен, как получить доступ к объекту панели из аннотации.

public class StatusValidator implements ConstraintValidator<NotNull, String> {

    @Override
    public void initialize(NotNull constraintAnnotation) {}

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if ("Canceled".equals(panel.status.getValue())) {
            if (value != null) {
                return true;
            }
        } else {
            return false;
        }
    }
}

Это panel.status.getValue(); дает мне проблемы.. не уверен, как это сделать.

4b9b3361

Ответ 1

В этом случае я предлагаю написать специальный валидатор, который будет проверять на уровне класса (чтобы мы могли получить доступ к полям объекта), что одно поле требуется, только если другое поле имеет определенное значение. Обратите внимание, что вы должны написать общий валидатор, который получает 2 имени поля и работает только с этими двумя полями. Чтобы потребовать больше одного поля, вы должны добавить этот валидатор для каждого поля.

Используйте следующий код в качестве идеи (я его не тестировал).

  • Интерфейс проверки.

    /**
     * Validates that field {@code dependFieldName} is not null if
     * field {@code fieldName} has value {@code fieldValue}.
     **/
    @Target({TYPE, ANNOTATION_TYPE})
    @Retention(RUNTIME)
    @Constraint(validatedBy = NotNullIfAnotherFieldHasValueValidator.class)
    @Documented
    public @interface NotNullIfAnotherFieldHasValue {
    
        String fieldName();
        String fieldValue();
        String dependFieldName();
    
        String message() default "{NotNullIfAnotherFieldHasValue.message}";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
    
        @Target({TYPE, ANNOTATION_TYPE})
        @Retention(RUNTIME)
        @Documented
        @interface List {
            NotNullIfAnotherFieldHasValue[] value();
        }
    
    }
    
  • Реализация валидатора

    /**
     * Implementation of {@link NotNullIfAnotherFieldHasValue} validator.
     **/
    public class NotNullIfAnotherFieldHasValueValidator
        implements ConstraintValidator<NotNullIfAnotherFieldHasValue, Object> {
    
        private String fieldName;
        private String expectedFieldValue;
        private String dependFieldName;
    
        @Override
        public void initialize(NotNullIfAnotherFieldHasValue annotation) {
            fieldName          = annotation.fieldName();
            expectedFieldValue = annotation.fieldValue();
            dependFieldName    = annotation.dependFieldName();
        }
    
        @Override
        public boolean isValid(Object value, ConstraintValidatorContext ctx) {
    
            if (value == null) {
                return true;
            }
    
            try {
                String fieldValue       = BeanUtils.getProperty(value, fieldName);
                String dependFieldValue = BeanUtils.getProperty(value, dependFieldName);
    
                if (expectedFieldValue.equals(fieldValue) && dependFieldValue == null) {
                    ctx.disableDefaultConstraintViolation();
                    ctx.buildConstraintViolationWithTemplate(ctx.getDefaultConstraintMessageTemplate())
                        .addNode(dependFieldName)
                        .addConstraintViolation();
                        return false;
                }
    
            } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) {
                throw new RuntimeException(ex);
            }
    
            return true;
        }
    
    }
    
  • Пример использования валидатора

    @NotNullIfAnotherFieldHasValue.List({
        @NotNullIfAnotherFieldHasValue(
            fieldName = "status",
            fieldValue = "Canceled",
            dependFieldName = "fieldOne"),
        @NotNullIfAnotherFieldHasValue(
            fieldName = "status",
            fieldValue = "Canceled",
            dependFieldName = "fieldTwo")
    })
    public class SampleBean {
        private String status;
        private String fieldOne;
        private String fieldTwo;
    
        // getters and setters omitted
    }
    

Обратите внимание, что реализация validator использует класс BeanUtils из библиотеки commons-beanutils, но вы также можете использовать BeanWrapperImpl из Spring Framework.

См. также этот отличный ответ: Подтверждение перекрестного поля с помощью Hibernate Validator (JSR 303)

Ответ 2

Определите метод, который должен быть проверен на true, и помещайте аннотацию @AssertTrue в начало страницы:

  @AssertTrue
  private boolean isOk() {
    return someField != something || otherField != null;
  }

Метод должен начинаться с 'is'.

Ответ 3

Вы должны использовать пользовательский DefaultGroupSequenceProvider<T>:

ConditionalValidation.java

// Marker interface
public interface ConditionalValidation {}

MyCustomFormSequenceProvider.java

public class MyCustomFormSequenceProvider
    implements DefaultGroupSequenceProvider<MyCustomForm> {

    @Override
    public List<Class<?>> getValidationGroups(MyCustomForm myCustomForm) {

        List<Class<?>> sequence = new ArrayList<>();

        // Apply all validation rules from ConditionalValidation group
        // only if someField has given value
        if ("some value".equals(myCustomForm.getSomeField())) {
            sequence.add(ConditionalValidation.class);
        }

        // Apply all validation rules from default group
        sequence.add(MyCustomForm.class);

        return sequence;
    }
}

MyCustomForm.java

@GroupSequenceProvider(MyCustomFormSequenceProvider.class)
public class MyCustomForm {

    private String someField;

    @NotEmpty(groups = ConditionalValidation.class)
    private String fieldTwo;

    @NotEmpty(groups = ConditionalValidation.class)
    private String fieldThree;

    @NotEmpty
    private String fieldAlwaysValidated;


    // getters, setters omitted
}

См. также связанный с этим вопрос.

Ответ 4

Другой подход заключался бы в создании (защищенного) геттера, который возвращает объект, содержащий все зависимые поля. Пример:

public class MyBean {
  protected String status;
  protected String name;

  @StatusAndSomethingValidator
  protected StatusAndSomething getStatusAndName() {
    return new StatusAndSomething(status,name);
  }
}

StatusAndSomethingValidator теперь может обращаться к StatusAndSomething.status и StatusAndSomething.something и выполнять зависимую проверку.