Bootstrap

springboot中使用自定义的HibernateValidator校验器

springboot中使用自定义的HibernateValidator校验器


目标:创建一个如下的注解,可以限制字符串值的内容是大写还是小写

  @NotBlank
  @CheckCase(value=CaseMode.LOWER)
  private String demoName;

物料准备:一个自定义注解,一个枚举类,一个自定义的ConstraintValidator校验器

定义自定义注解@CheckCase

package cn.ath.knowwikibackend.rest.hv;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

@Target({ElementType.FIELD,
        ElementType.METHOD,
        ElementType.PARAMETER,
        ElementType.ANNOTATION_TYPE,
        ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CheckCaseValidator.class)
@Documented
@Repeatable(CheckCase.List.class)
public @interface CheckCase {
    String message() default "CheckCase默认错误信息!";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    CaseMode value();

    @Target({ElementType.FIELD,
            ElementType.METHOD,
            ElementType.PARAMETER,
            ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @interface List{
        CheckCase[] value();
    }
}

定义1个枚举类,用于指定@CheckCase 是限制大写还是限制小写

package cn.ath.knowwikibackend.rest.hv;

public enum CaseMode {
    UPPER,
    LOWER;
}

自定义CheckCaseValidator校验器,定义具体的校验逻辑

package cn.ath.knowwikibackend.rest.hv;

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

/**
 * 自定义 hibernate ConstraintValidator 校验器
 */
public class CheckCaseValidator implements ConstraintValidator<CheckCase, String> {
    private CaseMode caseMode;
    /**
     * 定义需要校验的注解的类型
     * @param constraintAnnotation
     */
    @Override
    public void initialize(CheckCase constraintAnnotation) {
        this.caseMode = constraintAnnotation.value();
    }
    /**
     * 自定义 对目标参数的校验逻辑
     * @param value   待校验的字符串
     * @param context ConstraintValidatorContext
     * @return boolean
     */
    @Override
    public boolean isValid(String value,
                           ConstraintValidatorContext context) {
        //jakarata bean validation 建议null是有效的。如果null不是有效数据,则需要加上注解@NotNull
        if (value == null) {
            return true;
        }
        boolean flag;
        if(caseMode == CaseMode.UPPER){
            flag = value.equals(value.toUpperCase());
            if (!flag){
                //自定义抛出的 校验不通过时的提示信息内容
                context.disableDefaultConstraintViolation();
                context.buildConstraintViolationWithTemplate("字符串必须是大写")
                        .addConstraintViolation();
            }
        }else {
            flag = value.equals(value.toLowerCase());
            if (!flag){
                context.disableDefaultConstraintViolation();
                context.buildConstraintViolationWithTemplate("字符串必须是小写")
                        .addConstraintViolation();
            }
        }
        return flag;
    }
}

测试使用@CheckCase 注解

@PostMapping("/addUser")
public UserTestResp  addUser(@RequestBody @Validated
    UserTestReq1 req){
    log.info("req:{}",req);
    return UserTestResp.builder()
        .age(req.getAge())
        .name(req.getName())
        .build();
}

@Data
public class UserTestReq1 {
    @Length(max = 30,min=4)  //userTestReq1.name长度需要在4和30之间
    @NotBlank
    private String name;

    @Min(1)  //userTestReq1.age最小不能小于1
    @Max(120) //userTestReq1.age最大不能超过120
    @NotNull
    private Integer age;

    @NotBlank
    @CheckCase(value=CaseMode.LOWER)  //限制字符串内容是小写
    private String demoName;   
}

@Slf4j
@RestControllerAdvice(annotations = RestController.class)
public class RestGlobalExceptionHandler{
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public ResultVO<String> MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e)     {
        log.error("自动抛的MethodArgumentNotValidException",e);
        // 从异常对象中拿到ObjectError对象
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        log.error("objectError:{}", JSONObject.toJSONString(objectError, true));
        String field = String.valueOf(JSONPath.eval(objectError, "$.field"));
        log.error("field:{}", field);
        String objName = objectError.getObjectName();
        System.err.println(objName);
        // 然后提取错误提示信息进行返回
        return new ResultVO<>(ResultCodeEnum.VALIDATE_FAILED,
                objName + "." + field + objectError.getDefaultMessage());
    }
}

{
    "code": 9996,
    "msg": "参数校验异常",
    "content": "userTestReq1.demoName字符串必须是小写",
    "timestamp": 1685533250234,
    "traceDateTime": "2023-05-31 19:40:50"
}
;