springMvc自定义参数校验器及基础使用

学习链接

spring boot mvc Validator 自定义校验(枚举值校验)
SpringBoot 参数校验的方法
Spring Validation最佳实践及其实现原理
注解系列——自定义注解验证器

基础使用

1.单个参数校验

import javax.validation.constraints.NotNull;
@RestController
@RequestMapping("/vadmin/cart")
@Validated // 开启校验(在类上面不能用@Valid,否则下面的校验注解无效)
public class CartController {
    @RequestMapping("deleteCart")
    @ResponseBody              // 如果为null,这里将会抛出异常,可使用全局异常处理器捕获
    public Result deleteCart(@NotNull Long userId, @NotNull Long pid){ 
        cartService.deleteCart(userId, pid);
        return Result.succ(null);
    }
}
// 普通参数校验(如上),在controller类上加@Validated注解才能生效(不要在类上使用@Valid,否则无效)。 
// 如果将@Validated加在方法参数里面,普通参数校验将无效(不会做校验),
// 将@Validated加载controller类上并不会引起实体类的参数校验
//                        即(实体类中有属性有校验注解,但是controller的方法参数里面没有写@Validated)
// 如果校验失败, 将会抛出ConstraintViolationException异常
全局异常处理器
@ControllerAdvice
public class MyExceptionHandler {

    @ExceptionHandler(MyException.class)
    @ResponseBody
    public Result myExceptionResult(MyException ex){
        return Result.fail(ex.getMessage());
    }

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result exceptionResult(Exception ex){
        return Result.fail("内部错误: " + ex.getMessage());
    }

}

2.实体类参数校验

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;

@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_cart")
public class Cart implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "tc_id", type = IdType.AUTO)
    private Long tcId;

    private Long cartId;

    private Long userId;

    @NotNull(message = "商品ID不能为空")
    private Long pid;

    @Min(value = 1,message = "至少添加一件商品")
    @NotNull(message = "必须添加商品数量信息")
    private Long pcount;
    
}

@RequestMapping("addToCart")   
@ResponseBody           // 启用校验               // 校验结果封装          // 后面可以接参数
public Result addToCart(@Validated Cart cart, BindingResult bindingResult,Man man) {
    if (bindingResult.hasErrors()) {
        StringBuilder sb = new StringBuilder();
        bindingResult.getAllErrors().forEach(error->{
            sb.append(error.getDefaultMessage() + " | ");
        });
        throw new MyException(sb.toString());// 处理校验结果,抛出异常,让全局异常处理器处理自定义异常
    }
    cartService.addCart(cart);
    return Result.succ(null);
}
// 实体类校验注意事项
//    1. 必须把@Validated写在controller方法里面,写在类上面无效;
//    2. 一个校验的实体类对应一个BindingResult,否则会抛出MethodArgumentNotValidException异常(全局异常处理器处理)
//			如:(@Validated Person person,BindingResult bindingResult1,
//				@Validated Man man, BindingResult bindingResult) 是正确的
//			如:(@Validated Cart cart, BindingResult bindingResult,Man man) 是正确的
//          如:(@Validated Cart cart,Man man, BindingResult bindingResult) 是错误的
//          第2小点总结: 一个@Validated对应一个BindingResult,否则抛出异常
//     3. 如果需要把错误信息配置在配置文件中
//          在springboot的resources目录下新建【ValidationMessages.properties】
//                                    内容为:person.name.notnull=person.name用户名不能为空
//                                           man.name.notnull=man.name用户名名不能为空
//                   之后即可在实体类属性上引用: @NotNull(message = "{man.name.notnull}")
//     4. 如果前面有校验注解,但是后面如果不写BindingResult,controller就会抛出异常;
//                               如果写了BindingResult那么就会进controller方法的逻辑


3.嵌套校验

@PostMapping("saveCareOrders")   // 这里的@Validated触发对CareOrderFormDto类中的注解校验
public AjaxResult saveCareOrders(@Validated @RequestBody CareOrderFormDto careOrderFormDto) {
    
}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CareOrderFormDto implements Serializable {
    // 处方
    @Valid       // 在这里触发对CareOrderDto类中的注解校验
    @NotNull     // 如果这里不写@NotNull注解, 只写@Valid注解,那么如果前端如果根本就不传careOrderDto过来,
                 //                         那么就不会触发CareOrderDto类中的校验注解,传了的话,就做校验
                 // 所以建议加上@NotNull注解,先触发@NotNull的非空校验,再通过@Valid触发CareOrderDto类中的校验注解
    private CareOrderDto careOrderDto;
    // 处方详情
    @Valid       // 在这里触发对CareOrderItemDto类的注解校验
    @NotEmpty(message = "处方详情不能为空")  // 将会触发集合不能为空的校验
    private List<CareOrderItemDto> careOrderItemDtoList;
}

4.分组校验

1.实体类

@Data
public class Person { 
    // 读取resources目录下的ValidationMessages.properties文件 、归Agroup
    @NotNull(message = "{person.name.notnull}",groups = {Agroup.class})
    private String name;
    
    @NotNull(message = "{person.age.notnull}",groups = {Bgroup.class})
    private Integer age;
    
    // 不写分组,则默认属于Default.class组
    @NotNull(message = "address不能为空")
    private String address;
}

2.校验

@RequestMapping("test04") // 只会校验Agroup相关的
public String test04(@Validated({Agroup.class}) Person person,BindingResult result) {
    log.info("{},{}",person.getName(),person.getAge());
    return "ok";
}

@RequestMapping("test05") // 只会校验Bgroup相关的
public String test05(@Validated({Bgroup.class}) Person person,BindingResult result) {
    log.info("{},{}",person.getName(),person.getAge());
    return "ok";
}

@RequestMapping("test06") // 只会校验Agroup、Bgroup相关的
public String test06(@Validated({Agroup.class,Bgroup.class}) Person person,BindingResult result) {
    log.info("{},{}",person.getName(),person.getAge());
    return "ok";
}

@RequestMapping("test07") // 校验Agroup、Bgroup再加上 没有分组的校验注解【加上默认的Default.class组的】
public String test07(@Validated({Agroup.class,Bgroup.class, Default.class}) Person person, BindingResult result) {
    log.info("{},{}",person.getName(),person.getAge());
    return "ok";
}

@RequestMapping("test07") // 只会校验默认组的
public String test07(@Validated Person person, BindingResult result) {
    log.info("{},{}",person.getName(),person.getAge());
    return "ok";
}

5.自定义校验注解

1.自定义校验用法:

package com.zzhua.controller.validate;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CheckLenValidator.class)
public @interface CheckLen {

    String message() default "{字符串长度不符合要求}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
    
    int len();
}

public class CheckLenValidator implements ConstraintValidator<CheckLen, String> {
   private int len;
   public void initialize(CheckLen constraint) {
       this.len = constraint.len();
   }
   public boolean isValid(String obj, ConstraintValidatorContext context) {
      return obj != null && len == obj.length();
   }
}

@RestController
@RequestMapping("test")
@Validated // 必须加这个并且加到这个位置(加到下面的test方法中无效, 因为i参数不是实体类; 实体类是加在下面方法中,才能生效
           //                        并且这两种加的方法互不影响)
public class RoleController {
	@GetMapping("test")
    public AjaxResult test(@CheckLen(len=3) String i) {
        return AjaxResult.success("hello");
    }
}

2.内置的校验注解

// 查看 ConstraintHelper 这个类里面注册了很多 validator
// 这也是为什么内置校验注解没有校验器的原因 validatedBy={} 

自定义参数校验器

springmvc参数校验使用方法

我们先总结一下在springMvc使用注解来实现参数校验的方法

  • 在Controller接口上添加@Validated,可以触发在此controller类中,所有使用了校验注解的接口
    如:

    @Validated // 添加此注解
    @RestController
    public class LoginController {
    
    	// 对单个参数的校验
        @GetMapping("login/captcha")
        public Result<Object> captcha(@NotNull(message = "type不能为空") Integer type) {
            return loginService.captcha(type, request,response);
        }
    
    	// 对自定义类型参数校验
    	//(注意这里只会校验registerDTO是否为null,而不会触发在EmailRegisterDTO 类上的校验注解校验)
    	//(如果需要触发对EmailRegisterDTO 类上的校验注解校验,那么看下面的嵌套校验使用)
        @PostMapping("register/email") // 这里只是为了演示效果, 才把RequestBody的required属性置为false的。
        public Result<Boolean> registerEmail(@NotNull(message="参数不能为空")  // 此处触发了对registerDTO实体类的校验
                                             @RequestBody(required = false) EmailRegisterDTO registerDTO) {
            return loginService.registerEmail(registerDTO);
        }
    
    }
    
  • 在Controller类中的某个接口方法中在方法参数的前面加上@Valid或@Validate注解,即可触发该参数所对应的实体类A上的属性上所添加的校验注解。如果这个实体类A上的属性还是个实体类B(我们自己定义的类),那么可以继续在这个实体类A的这个属性(类型为实体类B)上添加@Valid注解,来触发对嵌套的实体类B上的加了校验注解的属性校验,即嵌套校验
    如:

    @Data
    public class EmailRegisterDTO {
    
        @Email
        private String email;
    
        private String emailCode;
    
        private String password;
    
        private String checkPassword;
    
    	@NotNull("customVo不能为空")
    	@Valid // 1. 触发嵌套校验, 不使用此注解,将不会触发CustomVo上的类上使用的校验注解的校验
    		   // 2. 如果没有@NotNull注解校验,而只有@Valid注解,那就是如果前端传了customVo就校验,没传,那就不校验
    	private CustomVo customVo;
    
    	@NotEmpty("customVos不能为空")
    	private List<CustomVo> customVos;
    }
    
    @Data
    public class CustomVo {
    
    	@NotBlank("attr不能为空")
    	private String attr;
    }
    
    
    // 需要在参数的前面加上@Valid注解,才能触发EmailRegisterDTO类上的校验注解的校验
    @PostMapping("register/email")
    public Result<Boolean> registerEmail(@Valid @NotNull(message="参数不能为空") @RequestBody(required = false) EmailRegisterDTO registerDTO) {
        return Result.ok(true);
    }
    

自定义错误消息模板

观察源码

我们注意到源码里面,就比如@NotNull这个注解,里面的message使用了大括号,来引用消息内容。错误消息的内容可以到org/hibernate/validator/ValidationMessages_zh_CN.properties文件中找到。

@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(List.class)
@Documented
@Constraint(validatedBy = { })
public @interface NotNull {

	String message() default "{javax.validation.constraints.NotNull.message}";

	Class<?>[] groups() default { };

	Class<? extends Payload>[] payload() default { };

	/**
	 * Defines several {@link NotNull} annotations on the same element.
	 *
	 * @see javax.validation.constraints.NotNull
	 */
	@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
	@Retention(RUNTIME)
	@Documented
	@interface List {

		NotNull[] value();
	}
}

ValidationMessages_zh_CN.properties消息的内容如下,可以看到里面

  • 可以使用{注解的属性}的方式引用注解里面的属性值,
  • 可以使用${validatedValue} 的方式引用待校验的值
  • ${..}中可以使用表达式
javax.validation.constraints.AssertFalse.message     = 只能为false
javax.validation.constraints.AssertTrue.message      = 只能为true
javax.validation.constraints.DecimalMax.message      = 必须小于或等于{value}
javax.validation.constraints.DecimalMin.message      = 必须大于或等于{value}
javax.validation.constraints.Digits.message          = 数字的值超出了允许范围(只允许在{integer}位整数和{fraction}位小数范围内)
javax.validation.constraints.Email.message           = 不是一个合法的电子邮件地址
javax.validation.constraints.Future.message          = 需要是一个将来的时间
javax.validation.constraints.FutureOrPresent.message = 需要是一个将来或现在的时间
javax.validation.constraints.Max.message             = 最大不能超过{value}
javax.validation.constraints.Min.message             = 最小不能小于{value}
javax.validation.constraints.Negative.message        = 必须是负数
javax.validation.constraints.NegativeOrZero.message  = 必须是负数或零
javax.validation.constraints.NotBlank.message        = 不能为空
javax.validation.constraints.NotEmpty.message        = 不能为空
javax.validation.constraints.NotNull.message         = 不能为null
javax.validation.constraints.Null.message            = 必须为null
javax.validation.constraints.Past.message            = 需要是一个过去的时间
javax.validation.constraints.PastOrPresent.message   = 需要是一个过去或现在的时间
javax.validation.constraints.Pattern.message         = 需要匹配正则表达式"{regexp}"
javax.validation.constraints.Positive.message        = 必须是正数
javax.validation.constraints.PositiveOrZero.message  = 必须是正数或零
javax.validation.constraints.Size.message            = 个数必须在{min}和{max}之间

org.hibernate.validator.constraints.CreditCardNumber.message        = 不合法的信用卡号码
org.hibernate.validator.constraints.Currency.message                = 不合法的货币 (必须是{value}其中之一)
org.hibernate.validator.constraints.EAN.message                     = 不合法的{type}条形码
org.hibernate.validator.constraints.Email.message                   = 不是一个合法的电子邮件地址
org.hibernate.validator.constraints.Length.message                  = 长度需要在{min}和{max}之间
org.hibernate.validator.constraints.CodePointLength.message         = 长度需要在{min}和{max}之间
org.hibernate.validator.constraints.LuhnCheck.message               = ${validatedValue}的校验码不合法, Luhn模10校验和不匹配
org.hibernate.validator.constraints.Mod10Check.message              = ${validatedValue}的校验码不合法, 模10校验和不匹配
org.hibernate.validator.constraints.Mod11Check.message              = ${validatedValue}的校验码不合法, 模11校验和不匹配
org.hibernate.validator.constraints.ModCheck.message                = ${validatedValue}的校验码不合法, ${modType}校验和不匹配
org.hibernate.validator.constraints.NotBlank.message                = 不能为空
org.hibernate.validator.constraints.NotEmpty.message                = 不能为空
org.hibernate.validator.constraints.ParametersScriptAssert.message  = 执行脚本表达式"{script}"没有返回期望结果
org.hibernate.validator.constraints.Range.message                   = 需要在{min}和{max}之间
org.hibernate.validator.constraints.SafeHtml.message                = 可能有不安全的HTML内容
org.hibernate.validator.constraints.ScriptAssert.message            = 执行脚本表达式"{script}"没有返回期望结果
org.hibernate.validator.constraints.URL.message                     = 需要是一个合法的URL

org.hibernate.validator.constraints.time.DurationMax.message        = 必须小于${inclusive == true ? '或等于' : ''}${days == 0 ? '' : days += '天'}${hours == 0 ? '' : hours += '小时'}${minutes == 0 ? '' : minutes += '分钟'}${seconds == 0 ? '' : seconds += '秒'}${millis == 0 ? '' : millis += '毫秒'}${nanos == 0 ? '' : nanos += '纳秒'}
org.hibernate.validator.constraints.time.DurationMin.message        = 必须大于${inclusive == true ? '或等于' : ''}${days == 0 ? '' : days += '天'}${hours == 0 ? '' : hours += '小时'}${minutes == 0 ? '' : minutes += '分钟'}${seconds == 0 ? '' : seconds += '秒'}${millis == 0 ? '' : millis += '毫秒'}${nanos == 0 ? '' : nanos += '纳秒'}
自定义错误消息
创建ValidationMessages.properties

在resources目录下,创建ValidationMessages.properties文件,内容可以如下:

## ${validatedValue}去引用待校验的值,而{min}引用校验注解实例的属性值
a.b.c = ${validatedValue}-{min}这个不该为空的

在类上添加校验注解,并自定义消息模板内容

@Data
public class EmailRegisterDTO {

    @Length(message = "${validatedValue}不是一个合法的值",min = 3)
    @NotNull(message = "这个@length不校验null,还得我出马")
    private String email;

	// 使用{errorMsgkey} 大括号的形式去引用写在ValidationProperties的模板消息,也可以用来引用校验注解的属性值
	// ${validatedValue}去引用待校验的值
    @Length(message = "{a.b.c}-${validatedValue}-${validatedValue.length()}-{min}!!!",min = 3)
    private String emailCode;
    
}
在Controller中使用
@Validated
@RestController
public class LoginController {

    @PostMapping("register/email")
    public Result<Boolean> registerEmail(@Valid @NotNull(message="参数不能为空") @RequestBody(required = false) EmailRegisterDTO registerDTO) {
        return Result.ok(true);
    }
    
}

自定义参数校验器

上面这种校验,它只能对某个实体的的单个属性作校验,但是有的时候,一个实体类的2个属性是互相关联的(比如,注册的时候,需要用户输入两次密码,一个为密码,第二个为确认密码,前端做是必要的,但后台也应该要做),这时候,我们只能被迫在业务代码里写了。但是我们可以通过自定义参数校验器实现

CheckEmailRegister
@Target({ElementType.TYPE,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CheckEmailRegisterValidator.class)
public @interface CheckEmailRegister {

    // 可参考 ValidationMessages_zh_CN.properties 文件中的写法,这里可以用el表达式的语法去写逻辑
    String message() default "${validatedValue}中的${validatedValue.email}不合法,${validatedValue.email==null||validatedValue.email==''?'邮箱不能为空(⊙﹏⊙)':''}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};


}
CheckEmailRegisterValidator
  • 通过initialize方法,可以获取校验注解实例,可以方便的获取注解的属性,可以保存下来,以作后面校验使用
  • isValid方法返回true或者false,表示是否校验通过,如果校验不通过,将会使用校验注解的message属性作为消息模板(功能同如上所述)
  • 可以使用ConstraintValidatorContext提供的api,来传入消息模板,自定义消息错误提示
@Slf4j
public class CheckEmailRegisterValidator implements ConstraintValidator<CheckEmailRegister, EmailRegisterDTO> {

    @Override
    public void initialize(CheckEmailRegister anno) {
    }

    @Override
    public boolean isValid(EmailRegisterDTO registerDTO, ConstraintValidatorContext context) {
        if (registerDTO == null) {
            return false;
        }

        try {

            // 这里不要给消息模板, 测试一下自定义注解的默认的消息模板
            if (StringUtils.isEmpty(registerDTO.getEmail()) || registerDTO.getEmail().length() < 2) {
                return false;
            }

            if (StringUtils.isEmpty(registerDTO.getEmailCode())) {
                return newErrorMsg(context,"邮箱验证码不能为空");
            }

            if (StringUtils.isEmpty(registerDTO.getPassword())) {
                return newErrorMsg(context,"密码不能为空");
            }

            if (StringUtils.isEmpty(registerDTO.getCheckPassword())) {
                return newErrorMsg(context,"密码不能为空");
            }

            if (!Objects.equals(registerDTO.getPassword(), registerDTO.getCheckPassword())) {
                return newErrorMsg(context,"两次输入密码不一致," +
                        "一个是:${validatedValue.password}," +
                        "一个是:${validatedValue.checkPassword}");
            }
        } catch (RuntimeException ex) {
            log.error("校验发生异常: {}", ex.getMessage());
            return false;
        }


        return true;
    }

    private boolean newErrorMsg(ConstraintValidatorContext context, String errMsg) {
        context.disableDefaultConstraintViolation();
        context.buildConstraintViolationWithTemplate(errMsg)
                .addConstraintViolation();
        return false;
    }

}
在controller中使用
@Validated
@RestController
public class LoginController {

	@ApiOperation("邮箱注册账号")
	@PostMapping("web/register/email") // @Controller上加了@Validated注解,触发了@CheckEmailRegister 的校验,
	                                   // 如果需要触发对registerDTO的类里面的属性上的校验注解的校验,这里需要加上@Valid
	public Result<Boolean> registerEmail(@CheckEmailRegister @RequestBody EmailRegisterDTO registerDTO) {
	    return loginService.registerEmail(registerDTO);
	}
}

自定义枚举传参的校验器

EmailCodeTypeEnum枚举类
@Getter
public enum EmailCodeTypeEnum {

    REGISTER(1, "registerTpl.ftl"),
    FIND_PWD(2, "findPwd.ftl"),
    ;

    private Integer type;

    private String templateName;


    EmailCodeTypeEnum(int type, String templateName) {
        this.type = type;
        this.templateName = templateName;
    }

    public static EmailCodeTypeEnum type(Integer type) {
        for (EmailCodeTypeEnum emailCodeTypeEnum : EmailCodeTypeEnum.values()) {
            if (Objects.equals(emailCodeTypeEnum.type, type)) {
                return emailCodeTypeEnum;
            }
        }
        throw new RuntimeException();
    }
}
@Enums校验注解
@Target({ElementType.TYPE,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EnumsValidator.class)
public @interface Enums {

    String message() default "非法值:${validatedValue},请传入指定范围内的枚举值";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    // 校验的枚举类
    Class<?> enumClazz();

    // 校验的枚举类的属性
    String enumField();

    // null是否合法
    boolean nullValid() default false;


}
EnumsValidator
public class EnumsValidator implements ConstraintValidator<Enums, Object> {

    // 校验的枚举类
    private Class<?> enumClazz;

    // 校验的枚举类的属性
    private Field enumField;

    private boolean nullValid;

    @Override
    public void initialize(Enums enums) {

        this.nullValid = enums.nullValid();


        Class<?> enumsClass = enums.enumClazz();
        if (!enumsClass.isEnum()) {
            throw new RuntimeException(MessageFormat.format("%s不是枚举类", enums.enumClazz().getName()));
        }
        this.enumClazz = enumsClass;

        try {
            Field field = enumClazz.getDeclaredField(enums.enumField());
            field.setAccessible(true);
            this.enumField = field;
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(MessageFormat.format("枚举类%s,无此%s属性", enums.enumClazz().getName(), enumField));
        }

    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {

        if (value == null) {
            return nullValid; // null是否合法
        }

        for (Object enumConstant : this.enumClazz.getEnumConstants()) {
            try {
                if (Objects.equals(enumField.get(enumConstant), value)) {
                    return true;
                }
            } catch (IllegalAccessException e) {
                return false;
            }
        }

        List<Object> enumList = Arrays.stream(this.enumClazz.getEnumConstants()).map(obj -> {
            try {
                return enumField.get(obj);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("枚举校验发生错误");
            }
        }).collect(Collectors.toList());
        context.disableDefaultConstraintViolation();
        context.buildConstraintViolationWithTemplate("允许的值为: "+ StringUtils.arrayToCommaDelimitedString(enumList.toArray()))
                .addConstraintViolation();

        return false;
    }


}

自定义枚举int值校验器

EnumsType
@Target({ElementType.TYPE,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EnumsTypeValidator.class)
public @interface EnumsType {

    String message() default "非法值:${validatedValue},请传入指定范围内的枚举值";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    int[] types();

    // null是否合法
    boolean nullValid() default false;

}
EnumsTypeValidator
public class EnumsTypeValidator implements ConstraintValidator<EnumsType, Integer> {

    private List<Integer> typeList = new ArrayList<>();

    private boolean nullValid;

    @Override
    public void initialize(EnumsType enums) {
        int[] types = enums.types();
        for (int type : types) {
            typeList.add(type);
        }
        Assert.notEmpty(this.typeList, "@EnumsType提供的枚举值不能为空数组");
        this.nullValid = enums.nullValid();
    }

    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {

        if (value == null) {
            return nullValid;
        }

        for (Object type : this.typeList) {
            if (Objects.equals(type, value)) {
                return true;
            }
        }

        context.disableDefaultConstraintViolation();
        context.buildConstraintViolationWithTemplate("非法值:${validatedValue},请传入" + Arrays.toString(this.typeList.toArray()))
            .addConstraintViolation();


        return false;
    }
}

使用示例
@Validated
@RestController
public class LoginController {

	// 下面的type参数只能传入指定的1,2
	@GetMapping("login/captcha")
	public Result<Map<String,Object>> captcha(@EnumsType(types = {1,2}) Integer type) {
	
	    return loginService.captcha(type, request,response);
	}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值