@Validated分组校验

发布时间 2023-06-12 16:15:48作者: Lian_tiam

分组检验

场景: 如新增id必需为空(数据库自增id), 修改id必需不为空

1.在实体类中新建接口作为分组,字段添加分组注解

public class Employee {  
  
    public interface Add{}  
  
    public interface Update{}  
  
    @Null(message = "新增ID必需为空", groups = {Add.class})  
    @NotNull(message = "更新ID必需不为空", groups = {Update.class})  
    private Long id;
    
}

2. Controller增加参数注解,指定启用分组

@PostMapping("/ee/add")  
public ServerResponseEntity<String> addEmployee(@RequestBody @Validated({Employee.Add.class}) Employee employee){  
    return ServerResponseEntity.success();  
}  
@PutMapping("/ee/update")  
public ServerResponseEntity<String> updateEmployee(@RequestBody @Validated({Employee.Update.class}) Employee employee){  
    return ServerResponseEntity.success();  
}

如果在Controller中的方法给参数指定了校验分组,那么实体类中标注了校验注解,但是没有指定分组的校验规则将不生效

没有指定分组的校验注解,都属于默认Default分组,想要增加默认的校验,添加Default分组即可

@PostMapping("/ee/add")  
public ServerResponseEntity<String> addEmployee(@RequestBody @Validated({Employee.Add.class, Default.class}) Employee employee){  
    return ServerResponseEntity.success();  
}  
@PutMapping("/ee/update")  
public ServerResponseEntity<String> updateEmployee(@RequestBody @Validated({Employee.Update.class, Default.class}) Employee employee){  
    return ServerResponseEntity.success();  
}

集合的分组校验

接收参数为List集合,分组校验会失效,需要自定义校验注解。创建注解@ValidListGroup,自定义一个属性表示用哪个分组对集合进行校验

自定义注解

package com.example.springquickdemo.annotation.validator;  
  
import javax.validation.Constraint;  
import javax.validation.Payload;  
import javax.validation.groups.Default;  
import java.lang.annotation.*;  
  
/**  
 * @author lian  
 */
@Target({ElementType.FIELD, ElementType.PARAMETER})  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
@Constraint(validatedBy = {ListGroupValidator.class})  
public @interface ValidListGroup {  
  
    /**  
     * 自定义分组校验属性  
     */  
    Class<?>[] groupings() default {Default.class};  
  
    String message() default "分组校验参数有误";  
  
    Class<?>[] groups() default {};  
  
    Class<? extends Payload>[] payload() default {};  
  
    /**  
     * 快速失败  
     */  
    boolean quickFail() default false;  
  
}

自定义校验器

package com.example.springquickdemo.annotation.validator;  
  
import com.example.springquickdemo.exception.ListGroupValidException;  
import com.example.springquickdemo.util.ValidatorUtils;  
  
import javax.validation.ConstraintValidator;  
import javax.validation.ConstraintValidatorContext;  
import javax.validation.ConstraintViolation;  
import java.util.HashMap;  
import java.util.List;  
import java.util.Map;  
import java.util.Set;  
  
/**  
 * @author lian  
 */public class ListGroupValidator implements ConstraintValidator<ValidListGroup, List> {  
  
    /**  
     * 自定义注解@ValidListGroup中指定的校验分组  
     */  
    private Class<?>[] groupings;  
  
    private boolean quickFail;  
  
    /**  
     * Initializes the validator in preparation for     */    @Override  
    public void initialize(ValidListGroup constraintAnnotation) {  
        groupings = constraintAnnotation.groupings();  
        quickFail = constraintAnnotation.quickFail();  
        ConstraintValidator.super.initialize(constraintAnnotation);  
    }  
  
    /**  
     * Implements the validation logic.     */    @Override  
    public boolean isValid(List value, ConstraintValidatorContext context) {  
        Map<Integer, Set<ConstraintViolation<Object>>> errors = new HashMap<>(16);  
        for (int i = 0; i < value.size(); i++) {  
            Object object = value.get(i);  
            // 用工具类获取validator对象,进行分组校验,返回错误结果  
            Set<ConstraintViolation<Object>> error = ValidatorUtils.validator.validate(object, groupings);  
            if (error.size() > 0) {  
                errors.put(i, error);  
                if (quickFail) {  
                    throw new ListGroupValidException(errors);  
                }  
            }  
        }  
        if (errors.size() > 0) {  
            throw new ListGroupValidException(errors);  
        }  
        return true;  
    }  
}


@Component  
public class ValidatorUtils {  
    public static Validator validator;  
  
    /**  
     * 注入Validator对象  
     */  
    @Autowired  
    public void setValidator(Validator validator) {  
        ValidatorUtils.validator = validator;  
    }  
}

自定义异常

@Getter  
@Setter  
public class ListGroupValidException extends RuntimeException {  
  
    private Map<Integer, Set<ConstraintViolation<Object>>> errors;  
  
    public ListGroupValidException(Map<Integer, Set<ConstraintViolation<Object>>> errors) {  
        this.errors = errors;  
    }  
  
}

异常处理器处理自定义异常

/**  
 * 处理自定义集合分组校验注解的校验失败异常  
 */  
@ExceptionHandler(ValidationException.class)  
public ResponseEntity<ServerResponseEntity<?>> handleValidException(ValidationException e) {  
    log.error("ValidationException ", e);  
  
    Map<Integer, Map<Path, String>> errorMap = new HashMap<>(8);  
    Throwable throwable = e.getCause();  
    ListGroupValidException exception = (ListGroupValidException) throwable;  
    Map<Integer, Set<ConstraintViolation<Object>>> errors = exception.getErrors();  
    // 将异常信息收集到Map,key为集合中校验失败元素的索引,value为校验失败字段和原因  
    errors.forEach((k, v) -> {  
        errorMap.put(k, v.stream().collect(Collectors.toMap(ConstraintViolation::getPropertyPath, ConstraintViolation::getMessage)));  
    });  
    return ResponseEntity.status(HttpStatus.OK)  
            .body(ServerResponseEntity.fail(ResponseEnum.METHOD_ARGUMENT_NOT_VALID, errorMap));  
}

在Controller对应的方法中使用自定义注解,已经可以实现集合的分组校验. 如果想要一旦对某个对象的某个字段校验失败,立即停止校验,返回错误,开启快速失败模式即可

@PostMapping("/ee/addList")  
public ServerResponseEntity<String> addEmployeeList(@RequestBody @ValidListGroup(groupings = {Employee.Add.class, Default.class}) List<Employee> employeeList) {  
    return ServerResponseEntity.success();  
}  
  
@PutMapping("/ee/updateList")  
public ServerResponseEntity<String> updateEmployeeList(@RequestBody @ValidListGroup(groupings = {Employee.Update.class, Default.class}) List<Employee> employeeList) {  
    return ServerResponseEntity.success();  
}

开启快速失败quickFail = true

@PutMapping("/ee/updateList")  
public ServerResponseEntity<String> updateEmployeeList(@RequestBody @ValidListGroup(groupings = {Employee.Update.class, Default.class}, quickFail = true) List<Employee> employeeList) {  
    return ServerResponseEntity.success();  
}