【问题标题】:Cross-parameter validator isvalid throwing a ConstraintViolationException跨参数验证器有效抛出 ConstraintViolationException
【发布时间】:2021-07-19 09:11:04
【问题描述】:

我有一个带有 post call 的控制器,它接收的不是 POJO,而是一个 HttpServletRequest 请求和一个 @ParamBody 正文:

class MyController {

@PostMapping("/entrypoint")
public ResponseEntity<String> hello(HttpServletRequest request, @ParamBody body){
 /* Many subcalls */
}

控制器将调用一些对服务和其他休息客户端的子调用以​​形成响应,但我们在中间完成了许多验证,我们希望将它们移动到验证器以进行排序。在主子调用之前,会调用一个小型外部服务,根据响应我们将考虑请求是否有效。我们也会将该子调用移至验证器,并将服务添加为 bean。稍后我们需要在控制器中使用来自该调用的响应。

为此,我创建了一个验证器及其接口:

    @Constraint(validatedBy = ParamConstraintValidator.class)
    @Target({ PARAMETERS })
    @Retention(RUNTIME)
    @Documented
    public @interface ParamConstraint{
    
        String message() default "default message";
    
        Class<?>[] groups() default { };
    
        Class<? extends Payload>[] payload() default { };
    }
    
    @SupportedValidationTarget(ValidationTarget.PARAMETERS)
    public class ParamConstraintValidator implements
            ConstraintValidator<ParamConstraint, Object[]> {
    
        @Autowire
        ClientCheck clientCheck;
    
        @Override
        public void initialize(ParamConstraint paramConstraint) {
        }
    
        @Override
        public boolean isValid(Object[] value, ConstraintValidatorContext context) {
            if ( value.length != 4 ) {
                context.disableDefaultConstraintViolation();
                context.buildConstraintViolationWithTemplate("Param number is wrong")
                        .addConstraintViolation();
                return false;
            }
    
            if ( value[0] == null || value[1] == null ) {
                context.disableDefaultConstraintViolation();
                context.buildConstraintViolationWithTemplate("Param contents are null")
                        .addConstraintViolation();
                return false;
            }
            /* More validations*/
            ClientToken ct = clientCheck.verify(value[0], value[1])
    
            /* More validations*/
            value[2] = ct;
            return true;
    }

由于我们需要将该客户端令牌传递给后续子调用,因此我已将其作为第三个参数添加到控制器的方法签名中(第四个参数是 BindingResult 以从上下文中检索错误)

    class MyController {
    

    @PostMapping("/entrypoint")
    @ParamConstraint
    public ResponseEntity<String> hello(HttpServletRequest request, @ParamBody String body,
    @Param(required = false) ClientToken ct, BindingResult result){
       try {
        if(result.hasErrors()) {
            throw new WhateverException("Custom error to be processed by a handler");
        }
     /* Many subcalls */
       }
       catch (Exception e) {
       }
    }

我注意到的第一件事是验证器中的 isValid 没有被调用。经过多次试验和错误后,我尝试将@Validation 注解添加到控制器类中,如下所示:

@Validation
MyController

只有这样 isValid 方法才开始起作用。我可以从 isValid 正确获取 clientToken。当我必须在 isValid 中返回 false 时,问题就来了。接下来发生的事情是没有我所期望的 ParameterException,而是一个我无法在控制器中捕获的 ConstraintViolationException。 BindingResult 似乎无法使用,所以我也不知道触发异常的错误是什么。看起来理论上的跨参数约束被转换为阻止我从控制器捕获 ViolationConstraintException 的类约束。

第一个问题:考虑到我在签名中有一个未注释的 HttpServletRequest、一个@ParamBody 正文和一个@Param 参数,是否可以为我的示例做一个跨参数验证器?

第二:如果是,为什么不调用 isValid 呢?我需要改变什么?

第三:在控制器中添加@Validation 是否会将我的跨参数验证器转换为一个类?

第四:如果我必须使用类验证器,因为我不能使用跨参数验证器:如何读取 ConstraintViolationException? ControllerHandler 是否足以捕获异常?如何填充 ConstraintViolationException 以了解应将哪个错误响应发送回客户端?

它有点长,因为这可能是一个极端情况。我在某处读过但没有来源证明跨参数验证仅适用于 @Param 但不适用于 @ParamBody 它将由不同的组件管理,可能不接受将其放入不同类型的 isValid 方法中参数。

我希望有人能真正提供帮助。联系我以获得任何澄清

【问题讨论】:

  • 用几乎完全相同的实现来面对这个问题。你有没有设法摆脱它。

标签: java spring spring-boot validation spring-mvc


【解决方案1】:

在我看来,您应该创建一个 DTO 来接收有效负载并创建 annotation 以基于另一个来验证属性,然后您应该能够注入 bindingresults,其中包括验证错误(如果有的话)或者您可以在控制器方法中进一步实现自己的验证逻辑。

【讨论】:

  • 打破向后兼容性超出了范围。如果我有不需要的 TokenClient 是有充分理由的。我的问题是关于至少一个 ParamBody 和一个 Param 的交叉参数验证器
猜你喜欢
  • 2017-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-17
相关资源
最近更新 更多