当前日期时间不应通过使用@Past 注释的验证
Current datetime shouldnt pass the validation using @Past annotation
当字段设置为 now
时,我需要 @Past 来出错。我意识到字段上的 now
值和验证器比较时使用的 now
值会略有不同,因此需要在休眠验证器中设置容差。
问题是我无法让它工作。这是 junit:
@Test
public void testHibernateValidator_withPast_withTodayDate() {
// populates with 'now'
MyFormWithPast form = new MyFormWithPast();
form.setDt(OffsetDateTime.now(Clock.systemUTC()));
ValidatorFactory factory = Validation.byProvider(HibernateValidator.class)
.configure()
.clockProvider(() -> Clock.systemUTC())
// adds tolerance so that when comparing, the form dt and 'now' is considered equal,
// therefore dt is not a past datetime
.temporalValidationTolerance(Duration.ofMinutes(1))
.buildValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<MyFormWithPast>> errors = validator.validate(form);
// needs to fail, since 'now' shouldn't be considered 'past'
assertFalse("now shoudnt be considered as Past", errors.isEmpty());
}
public static class MyFormWithPast {
@Past
private OffsetDateTime dt;
public void setDt(OffsetDateTime dt) {
this.dt = dt;
}
public OffsetDateTime getDt() {
return dt;
}
}
我希望在字段中输入 'now' 时验证失败,因为 'now' 不应被视为 'past'。我错过了什么?
时间验证容差被设计得更宽松,而不是更严格。你希望它更严格。
我认为你需要自己的约束来处理你想做的事情。
只想分享我当前的解决方案,添加 1 分钟的默认前向公差,以便输入 'now' 不被视为 'past'。
注解:
/**
* Validates that the date is of the past, with forward tolerance of 1 minute,
* to offset the time to create a 'now' instance to compare to.
* The usage is when user selects 'today' in the UI, we dont want it to be considered as 'Past'
*
* Annotation is applicable to {@link OffsetDateTime}.
*/
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy=StrictPastValidator.class)
public @interface StrictPast {
public static final String MESSAGE = "{constraints.StrictPast.message}";
/**
* @return The error message template.
*/
String message() default MESSAGE;
/**
* @return The groups the constraint belongs to.
*/
Class<?>[] groups() default { };
/**
* @return The payload associated to the constraint
*/
Class<? extends Payload>[] payload() default {};
}
验证者:
public class StrictPastValidator implements ConstraintValidator<StrictPast, Object> {
@Override
public void initialize(StrictPast annotation) {
}
@Override
public boolean isValid(Object input, ConstraintValidatorContext ignored) {
if (input == null) {
return true;
} else if (input instanceof OffsetDateTime) {
return isValidOffsetDateTime((OffsetDateTime) input);
}
throw new IllegalStateException("StrictPastValidator is not applicable to the field type " + input.getClass().getName());
}
private boolean isValidOffsetDateTime(OffsetDateTime input) {
OffsetDateTime plusSecondsDt = input.plusSeconds(Duration.ofMinutes(1).getSeconds());
return plusSecondsDt.isBefore(OffsetDateTime.now(Clock.systemUTC()));
}
}
当字段设置为 now
时,我需要 @Past 来出错。我意识到字段上的 now
值和验证器比较时使用的 now
值会略有不同,因此需要在休眠验证器中设置容差。
问题是我无法让它工作。这是 junit:
@Test
public void testHibernateValidator_withPast_withTodayDate() {
// populates with 'now'
MyFormWithPast form = new MyFormWithPast();
form.setDt(OffsetDateTime.now(Clock.systemUTC()));
ValidatorFactory factory = Validation.byProvider(HibernateValidator.class)
.configure()
.clockProvider(() -> Clock.systemUTC())
// adds tolerance so that when comparing, the form dt and 'now' is considered equal,
// therefore dt is not a past datetime
.temporalValidationTolerance(Duration.ofMinutes(1))
.buildValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<MyFormWithPast>> errors = validator.validate(form);
// needs to fail, since 'now' shouldn't be considered 'past'
assertFalse("now shoudnt be considered as Past", errors.isEmpty());
}
public static class MyFormWithPast {
@Past
private OffsetDateTime dt;
public void setDt(OffsetDateTime dt) {
this.dt = dt;
}
public OffsetDateTime getDt() {
return dt;
}
}
我希望在字段中输入 'now' 时验证失败,因为 'now' 不应被视为 'past'。我错过了什么?
时间验证容差被设计得更宽松,而不是更严格。你希望它更严格。
我认为你需要自己的约束来处理你想做的事情。
只想分享我当前的解决方案,添加 1 分钟的默认前向公差,以便输入 'now' 不被视为 'past'。
注解:
/**
* Validates that the date is of the past, with forward tolerance of 1 minute,
* to offset the time to create a 'now' instance to compare to.
* The usage is when user selects 'today' in the UI, we dont want it to be considered as 'Past'
*
* Annotation is applicable to {@link OffsetDateTime}.
*/
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy=StrictPastValidator.class)
public @interface StrictPast {
public static final String MESSAGE = "{constraints.StrictPast.message}";
/**
* @return The error message template.
*/
String message() default MESSAGE;
/**
* @return The groups the constraint belongs to.
*/
Class<?>[] groups() default { };
/**
* @return The payload associated to the constraint
*/
Class<? extends Payload>[] payload() default {};
}
验证者:
public class StrictPastValidator implements ConstraintValidator<StrictPast, Object> {
@Override
public void initialize(StrictPast annotation) {
}
@Override
public boolean isValid(Object input, ConstraintValidatorContext ignored) {
if (input == null) {
return true;
} else if (input instanceof OffsetDateTime) {
return isValidOffsetDateTime((OffsetDateTime) input);
}
throw new IllegalStateException("StrictPastValidator is not applicable to the field type " + input.getClass().getName());
}
private boolean isValidOffsetDateTime(OffsetDateTime input) {
OffsetDateTime plusSecondsDt = input.plusSeconds(Duration.ofMinutes(1).getSeconds());
return plusSecondsDt.isBefore(OffsetDateTime.now(Clock.systemUTC()));
}
}