【发布时间】:2020-11-08 20:46:10
【问题描述】:
我正在 spring-boot 2.3.1.RELEASE Web 应用程序中验证 REST 服务请求/bean。目前,我正在使用 Hibernate Validator,尽管我愿意使用任何其他方式进行验证。
假设,我有一个模型Foo,我在Rest Controller 中收到它作为请求。我想验证completionDate 是否不是null 那么status 应该是“完成”或“关闭”。
@StatusValidate
public class Foo {
private String status;
private LocalDate completionDate;
// getters and setters
}
我创建了一个自定义类级别注释@StatusValidate。
@Constraint(validatedBy = StatusValidator.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface StatusValidate {
String message() default "default status error";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
我创建了StatusValidator 类。
public class StatusValidator implements ConstraintValidator<StatusValidate, Foo> {
@Override
public void initialize(StatusValidateconstraintAnnotation) {
}
@Override
public boolean isValid(Foovalue, ConstraintValidatorContext context) {
if (null != value.getCompletionDate() && (!value.getStatus().equalsIgnoreCase("complete") && !value.getStatus().equalsIgnoreCase("closed"))) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()).
.addPropertyNode("status").addConstraintViolation();
return false;
}
return true;
}
}
当我验证Foo 对象时(通过使用@Valid 或@Validated 或手动调用validator.validate() 方法),我在ConstraintViolation 中获得以下数据。
代码:
// Update.class is a group
Set<ConstraintViolation<Foo>> constraintViolations = validator.validate(foo, Update.class);
constraintViolations.forEach(constraintViolation -> {
ErrorMessage errorMessage = new ErrorMessage();
errorMessage.setKey(constraintViolation.getPropertyPath().toString());
errorMessage.setValue(constraintViolation.getInvalidValue());
// Do something with errorMessage here
});
constraintViolation.getPropertyPath().toString() => 状态
constraintViolation.getInvalidValue() =>(Foo 对象)
如何在自定义ConstraintValidator 或其他任何地方设置无效值(status 属性的实际值),以便constraintViolation.getInvalidValue() 返回status 属性的值?
或
在一个属性的验证取决于另一个属性的值的情况下,是否有更好的方法来验证请求负载/bean?
编辑: 我可以做类似的事情
if(constraintViolation.getPropertyPath().toString().equals("status")) {
errorMessage.setValue(foo.getStatus());
}
但这将涉及在某处维护属性名称的字符串常量,例如。 "status"。不过,在StatusValidator 中,我也在设置属性名称.addPropertyNode("status"),这也是我想避免的。
总结: 我正在寻找解决方案(不一定使用自定义验证或休眠验证器),其中
- 我可以验证 json 请求者或 bean,以验证其验证取决于其他属性值的属性。
- 我不必在任何地方将 bean 属性名称维护为字符串常量(维护噩梦)。
- 我能够获取无效的属性名称和值。
【问题讨论】:
-
您是否尝试检查
constraintViolation.getInvalidValue()是instantOfFoo,并对其进行转换并获取status 的值? -
感谢您的评论。我可以这样做,但我将拥有多个自定义验证注释和约束验证器。这将涉及对
constraintViolation.getPropertyPath()执行if else,然后根据它从Foo对象中获取值。我试图找到一种方法来避免这个手动过程。 -
你需要什么价值(你是如何使用它的)?如果您需要将其嵌入到违规消息中,您可以简单地使用
context.unwrap(HibernateConstraintValidatorContext.class).addExpressionVariable("status", value.getStatus())并使用${status}在验证消息中引用它 -
至于你所说的“维护噩梦”,可以使用Lombok's
@FieldNameConstants -
感谢@crizzis 的评论,我需要在json 错误响应中以单独的键发送属性名称和值。
标签: java spring-boot bean-validation