将自定义参数传递给 ConstraintValidator

Pass custom parameter to ConstraintValidator

我正在开发这个 springboot 应用程序,我需要对从 http 调用传递的值进行一些验证,我正在使用 class 级别验证 as explained here.

我正在使用这样的想法:

@ValidRequest
public class EventRequest {
    String date;
}

Response create(@Valid EventRequest request) {
     ..
}
Response update(Long entityId, @Valid EventRequest request) {
     ...
}

public class ValidRequestValidator 
  implements ConstraintValidator<ValidRequest, EventRequest> {

在 class ValidRequestValidator 中,我实现了 ConstraintValidator 接口,我需要检查数据库中是否有另一个 Event 实体在字段日期上满足某些条件。当我想创建一个新实体时很简单,我执行一个查询,但是当我需要更新时我需要排除我当前正在尝试更新的实体。

有没有办法将 entityId 参数传递给@ValidRequest 自定义验证器? 我知道一种方法是将字段 entityId 添加到 class EventRequest,但我想保持这种分离,因为 entityId 来自查询参数。

感谢您的帮助!

除了 field-specific(Single Parameter Constraint) 之外,您还可以为整个方法实施约束 (Cross-Parameter Constraint)。这将提供将特定方法的所有参数传递给验证器的能力。
注解定义:
注释使用了两个验证器,可以应用于方法或类型。

@Constraint(validatedBy = {ValidRequestMethodValidator.class, ValidRequestTypeValidator.class})
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidRequest {
    String message() default "Request is invalid!";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    ConstraintTarget validationAppliesTo() default ConstraintTarget.IMPLICIT;
}

将处理单个参数的约束验证器:

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class ValidRequestTypeValidator implements ConstraintValidator<ValidRequest, EventRequest> {
    @Override
    public boolean isValid(EventRequest request, ConstraintValidatorContext constraintValidatorContext) {
       // logic here
        return false;
    }
}

将处理特定方法的所有参数的约束验证器:

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.constraintvalidation.SupportedValidationTarget;
import javax.validation.constraintvalidation.ValidationTarget;

@SupportedValidationTarget(ValidationTarget.PARAMETERS)
public class ValidRequestMethodValidator implements ConstraintValidator<ValidRequest, Object[]> {
    @Override
    public boolean isValid(Object[] objects, ConstraintValidatorContext constraintValidatorContext) {
        Long entityId = null;
        EventRequest eventRequest = null;

        if (objects[0] instanceof Long) {
            entityId = (Long) objects[0];
        }
        if (objects[0] instanceof EventRequest) {
            eventRequest = (EventRequest) objects[0];
        }
        if (objects[1] instanceof EventRequest) {
            eventRequest = (EventRequest) objects[1];
        }
        //logic here
        return false;
    }
}

请注意, 我们必须使用 @org.springframework.validation.annotation.Validated 注释来注释应验证的 bean,以使方法验证器自动工作。

用法示例:

  1. 混合使用,@ValidRequest 在方法和单个参数级别定义注释。
@ValidRequest
public class EventRequest {
    public String value;
}

@RestController
@Validated
public class Controller {

    Response create(@Valid EventRequest request) {
     return new Response();
    }

    @ValidRequest(validationAppliesTo = ConstraintTarget.PARAMETERS)
    Response update(Long entityId, EventRequest request) {
        return new Response();
    }
}

对于create方法ValidRequestTypeValidator将被执行。
对于update方法ValidRequestMethodValidator 将被执行。


2。仅为方法定义注解

@RestController
@Validated
public class Controller {

    @ValidRequest(validationAppliesTo = ConstraintTarget.PARAMETERS)
    Response create(EventRequest request) {
     return new Response();
    }

    @ValidRequest(validationAppliesTo = ConstraintTarget.PARAMETERS)
    Response update(Long entityId, EventRequest request) {
        return new Response();
    }
}

对于create方法ValidRequestMethodValidator 将用一个元素对象数组执行
对于update方法ValidRequestMethodValidator 将使用两个元素对象数组

执行


3。同时为单个参数和方法定义注解

@ValidRequest
public class EventRequest {
    public String value;
}

@RestController
@Validated
public class Controller {

    @ValidRequest(validationAppliesTo = ConstraintTarget.PARAMETERS)
    Response update(Long entityId, @Valid EventRequest request) {
        return new Response();
    }
}

首先将执行单参数验证器ValidRequestTypeValidator
如果它将通过验证,则将执行第二个方法验证器 ValidRequestMethodValidator

可能只有一个 method-level 验证就足以解决您的问题。我描述了所有变体,仅供参考可能会有用。