1、 导读

在springboot 中,校验对象的参数值时,如果某个参数的值是固定的几个,那么可以使下面的枚举校验器 或 固定值校验证器。这两种验证器目录适用于参数值是数字、字符串,也可以进行拓展。

下面举例介绍的 自定义枚举校验固定值校验证的适用场景基本一样。

2、业务说明

2.1、业务场景1

邮寄状态 postStatus 的取值限定在范围是 0、1、2、3 。
其中:
0 :表示 未邮寄
1 :表示 已邮寄
2 :表示 收到货
3 :表示 邮寄出错

2.2、业务场景2

证件类型 cardType 的取值 限定范围是 A、B、C、D、E、F 。
其中:
A :表示 身份证
B :表示 护照
C :表示 军官
D :表示 台湾居民来往大陆通行证
E :表示 港澳居民来往内地通行证
F :表示 外国人永久居留

3、使用自定义枚举校验器

3.1、 定义通用的枚举校验器 EnumValueValidator

这个枚举校验器是通用的,正常情况下,一个项目中只要写这一枚举校验器即可。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;

@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EnumValidator.Validator.class)
/**
 * 枚举校验器
 */
public @interface EnumValidator {

	String message() default "{ EnumValidator's value is invalid }";

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

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

	Class<? extends EnumValidations.Enumerable> enumClass();

	class Validator implements ConstraintValidator<EnumValidator, Object> {

		private Class<? extends EnumValidations.Enumerable> enumClass=null;
		
		@Override
		public void initialize(EnumValidator enumValue) {
			enumClass = enumValue.enumClass();
		}

		@Override
		public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) {
			if (value == null) {
				return Boolean.FALSE;
			}
			try {
				EnumValidations.Enumerable result = off(enumClass,String.valueOf(value));
				return result == null ? false : true;
			} catch (Exception e) {
				e.printStackTrace();
				return Boolean.FALSE;
			}
		}

		public static <E extends EnumValidations.Enumerable> E off(@Nonnull Class<E> classType, String value) {
			for (E enumConstant : classType.getEnumConstants()) {
				if (enumConstant.getCode().equals(value)) {
					return enumConstant;
				}
			}
			return null;
		}
	}
}

3.2、定义枚举值

定义枚举类:



// 所有的枚举校验都在此文件中
public class EnumValidations {

	public interface  Enumerable{
		public String getCode();
		public String getText() ;
	}
	
	/**
	 *  邮寄状态
	 */
	public enum PostStatusEnum  implements Enumerable  {		
		//0:未邮寄, 1:已邮寄,2:收到货,3:邮寄出错
		A("0", "未邮寄"),
		B("1", "已邮寄"),
		C("2", "收到货"),
		D("3", "邮寄出错");
	
		private String code;	
		private String desc;	
	
		private PostStatusEnum(String code, String desc) {
			this.code = code;
			this.desc = desc;
		}	
	
		public String getCode() {
			return code;
		}	
	
		public String getDesc() {
			return desc;
		}	
	}
	
	/**
	 *用户信息 —— 证件类型  CardType
	 */
	public enum UserInfoCardTypeEnum   implements Enumerable  {	
		
		A("A", "身份证"),
		B("B", "护照"),
		C("C", "军官"),
		D("D", "台湾居民来往大陆通行证"),
		E("E", "港澳居民来往内地通行证"),
		F("F", "外国人永久居留")	;
	
		private String code;
		private String desc;
		
		private UserInfoCardTypeEnum(String code, String desc) {
			this.code = code;
			this.desc = desc;
		}	
	
		public String getCode() {
			return code;
		}	
		public String getDesc() {
			return desc;
		}
	}
}

3.3、 使用校验器

public class UpdateOrderStatusVO implements Serializable {	
 
    /**
     * 邮寄状态:0:未邮寄, 1:已邮寄,2:收到货,3:邮寄出错
     */
    @NotNull(message="邮寄状态不能为空")
    @EnumValidator(enumClass = EnumValidations.PostStatusEnum.class,message="邮寄状态取值不正确")
    private String postStatus;

    /**证件类型 */
    @NotNull(message="证件类型不能为空")
    @EnumValidator(enumClass = EnumValidations.UserInfoCardTypeEnum.class,message="证件类型取值不正确")
    private String  cardType;

   // getter/setter
   
}

使用 枚举校验 会出现 大量的重复代码 ,比如 属性、构造函数、 getter/setter 等。
下面推荐一种更优化方法。

4、使用 固定值校验证器(推荐)

4.1、定义 固定值校验证器 FixedValueValidator

import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {FixedValueValidator.FixedValueValid.class})

public @interface FixedValueValidator {

    String message() default "FixedValue's value is invalid";

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

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

    String[] fixedValue();

    class FixedValueValid implements ConstraintValidator<FixedValueValidator, Object> {

        String[] fixedValue = null;

        public void initialize(FixedValueValidator validData) {
            this.fixedValue = validData.fixedValue();
        }

        public boolean isValid(Object value, ConstraintValidatorContext constraintContext) {
            if (fixedValue == null || fixedValue.length == 0 ) {
                return false;
            }

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

            boolean flag = false;
            for (String str : fixedValue) {
                if (String.valueOf(value).equals(String.valueOf(str))) {
                    flag = true;
                    break;
                }
            }
            return flag;
        }
    }
}

4.2、使用 固定值校验证

public class UpdateOrderStatusVO implements Serializable {	
 
    /** 邮寄状态 */
    @NotNull(message="邮寄状态不能为空")
    @FixedValueValidator(fixedValue ={"0", "1", "2", "3" }, message="邮寄状态值错误" )
    private String postStatus;


    /** 证件类型 */
    @NotNull(message="证件类型不能为空")
    @FixedValueValidator(fixedValue ={"A", "B", "C", "D", "E", "F"}, message="证件类型值错误")
    private String  cardType;

   // getter/setter   
}

5、 捕获错误

方法1: 通过 BindingResult 捕获错误

@RequestMapping("/order/updateStatus")
public void save(@Validated UpdateOrderStatusVO vo, BindingResult bindingResult,@Validated Student student, BindingResult bindingResult2) {
    if (bindingResult.hasErrors()) {
        for (ObjectError error : bindingResult.getAllErrors()) {
            System.out.println(error.getDefaultMessage());
        }

    }
    System.out.println("-----------");

    if (bindingResult2.hasErrors()) {
        for (ObjectError error : bindingResult2.getAllErrors()) {
            System.out.println(error.getDefaultMessage());
        }

    }
}

方法2:通过 ConstraintViolationException 捕获错误(推荐)

@RequestMapping("/order/updateStatus")
public void save(@Validated UpdateOrderStatusVO vo,@Validated Student student) {
   // 。。。。
}

统一的异常的捕获类:

@ControllerAdvice
@Component
public class GlobalExceptionHandler {

	/**
	 * 拦截捕捉 MissingServletRequestParameterException 异常
	 */
	@ResponseBody
	@ExceptionHandler(value = MissingServletRequestParameterException.class)
	public String doMissingServletRequestParameterHandler(MissingServletRequestParameterException exception) {
		MissingServletRequestParameterException exs = (MissingServletRequestParameterException) exception;
		System.err.println(exs.getMessage());
		return "bad request, ";
	}

	/**
	 * 拦截捕捉 ConstraintViolationException 异常
	 */
	@ResponseBody
	@ExceptionHandler(value = ConstraintViolationException.class)
	public Map ConstraintViolationExceptionHandler(ConstraintViolationException ex) {
		Map map = new HashMap();
		Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
		Iterator<ConstraintViolation<?>> iterator = constraintViolations.iterator();
		List<String> msgList = new ArrayList<>();
		while (iterator.hasNext()) {
			ConstraintViolation<?> cvl = iterator.next();
			System.err.println(cvl.getMessageTemplate());
			msgList.add(cvl.getMessageTemplate());
		}
		map.put("status", 500);
		map.put("msg", msgList);
		return map;
	}
}

6、总结

通过对两个校验器的使用、比较,发现 固定值校验器 使用更加的简单,不需要创建类、属性、构造方法和getter/setter 等。

7、maven 依赖

javaBean hibernate Validation: https://blog.csdn.net/xiaojin21cen/article/details/90572834

8、相关文章

SpringMVC/Boot中的校验框架 @Valid 和 @Validated的使用: https://blog.csdn.net/xiaojin21cen/article/details/90572980

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐