一、背景

在我们系统中,有部分字段的值是枚举类型的,但是请求参数中一般不会直接使用枚举来进行接收,而是使用Interget等类型来接收,当系统中这些值是必须的时候,我们要保证前端系统传递的数据是正确的,合法的,因此需要进行校验。

例子:
比如:用户的性别 Sex 只能是 0-未知 1-男 2-女,那么前端只能传递0,1,2其中的一个,如果是别的值,则告知前端用户性别有问题。

二、技术要点

1、自己的验证逻辑类需要实现ConstraintValidator接口。
2、自定义一个注解,注解上需要使用@Constraint(validatedBy = xxx),validatedBy的值指向验证的类,即实现了ConstraintValidator接口的类。

三、实现一个自定义枚举校验。

1、需求。

我们有一个创建学生的接口,请求参数有一个 sex 值,它的值只能是0-未知 1-男 2-女,在控制层基于自定义的枚举注解,验证 sex 的值是否合法。

2、实现步骤

1、自定义一个 Sex 枚举。

此枚举,主要用于记录 sex 属性的值可以是哪些值。

注意:
我们的 枚举类中的 code 的值,验证的时候需要用到这个。


@Getter
@AllArgsConstructor
public enum Sex { UNKNOWN(0,"未知"),
MAN(1,"男"),
WOMEN(2,"女"); private final Integer code;
private final String desc;
}

2、自定义一个 Enum 注解


import com.google.common.collect.Lists;
import com.xincheng.common.exception.BizException;
import com.xincheng.xxcloud.ehouse.common.EhouseErrorCode;
import lombok.extern.slf4j.Slf4j; import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;
import java.lang.annotation.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List; /**
* @author huan.fu 2021/4/1 - 下午3:35
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
@Constraint(validatedBy = EnumValidator.class)
public @interface Enum {
/**
* 枚举的类型
*/
Class<?> value(); /**
* 错误消息
*
* @return
*/
String message() default "枚举类型的值不正确"; /**
* 获取枚举值的方法
*/
String method() default "getCode"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {};
}

解释:
1、@Constraint(validatedBy = EnumValidator.class) 中的validatedBy指定的是 @Enum 这个注解交由那个类去校验。
2、Class<?> value() 表示需要这个字段对应的枚举的类型。
3、String method() default “getCode” 这个 method() 方法表示,我们怎么从具体的枚举对象中获取值。

比如上方定义的Sex,里面有2个属性codedesc,而code作为枚举的值,此处的method() 就需要写 getCode

3、编写具体的验证逻辑类


@Component
@Slf4j
class SpringBean {
public void invoked() {
log.info("调用spring管理的bean的方法");
}
} /**
* 枚举校验
*/
@Slf4j
class EnumValidator implements ConstraintValidator<Enum, Object> { // 存具体枚举的值
private final List<Object> values = Lists.newArrayList(); @Autowired
private SpringBean springBean; @Override
public void initialize(Enum constraintAnnotation) { springBean.invoked(); Class<?> enumClazz = constraintAnnotation.value();
Object[] enumConstants = enumClazz.getEnumConstants();
if (null == enumConstants) {
return;
}
Method method = BeanUtils.findMethod(enumClazz, constraintAnnotation.method());
if (null == method) {
log.warn("枚举对象:[{}]中不存在方法:[{}],请检查.", enumClazz.getName(), constraintAnnotation.method());
throw new BizException(EhouseErrorCode.FAIL.getCode(), "枚举对象中不存在获取值的方法");
} method.setAccessible(true);
try {
for (Object enumConstant : enumConstants) {
values.add(method.invoke(enumConstant));
}
} catch (IllegalAccessException | InvocationTargetException e) {
log.warn("获取枚举类:[{}]的枚举对象的值失败.", enumClazz);
throw new BizException(EhouseErrorCode.FAIL.getCode(), "获取枚举值失败");
}
} @Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
return null == value || values.contains(value);
}
}

注意:

1、ConstraintValidator<Enum, Object>

第一个参数:是我们自定义的校验注解,此处是 Enum,是因为我们上方自定义的注解就是 @interface Enum 。
第二个参数:指的是页面上传递过来的具体的数据的类型。

2、如果我们的LocalValidatorFactoryBeanSpringConstraintValidatorFactory,那么在我们的验证类中可以使用Spring的依赖注入。

3、isValid 方法需要保证线程安全,因为它可能是多线程调用。

4、编写一个web请求,添加学生。

1、创建请求参数实体类


@Data
public class Student {
@Enum(value = Sex.class, message = "请填写正确的心别")
private Integer sex;
}

注意:
1、sex属性上使用了@Enum来标识,表示后期需要使用@Enum这个来验证,而我们自己写的EnumValidator是用来验证这个的。那么我们的sex属性的值匹配上来哪些值是合法的呢,这个可以看到@Enum的value上指定了value = Sex.class,即我们的sex的值需要是Sex这个枚举类的值的其中一个。

2、编写访问方法


public String addStudent(@Valid @RequestBody Student student) {
log.info("student:[{}]", student);
return "ok";
}

3、页面访问

1、sex 属性的值在 Sex 枚举的范围之内

2、sex 属性的值不在 Sex 枚举的范围之内

四、对应关系

五、参考文档

1、https://docs.spring.io/spring-framework/docs/5.3.x/reference/html/core.html#validation-beanvalidation-spring-constraints

基于自定义Validator来验证枚举类型的更多相关文章

  1. 实现自定义集合的可枚举类型(IEnumerable)和枚举数(IEnumerator )

    下面的代码示例演示如何实现自定义集合的 IEnumerable 和 IEnumerator 接口: using System; using System.Collections; using Syst ...

  2. angular4 自定义表单验证Validator

    表单的验证条件有时候满足不了需求就可以自定义验证 唯一要求返回是ValidatorFn export interface ValidatorFn{ (c:AbstractControl):Valida ...

  3. 自定义枚举类型的常用操作-附源码(xjl456852原创)

    自定义枚举类型中,假如我们有name和desc这样的属性,并在这个基础上定义了多个对象. 那么就可能用到通过name获取desc,或者通过desc获取name.通过name或者desc获取对应的枚举对 ...

  4. mybatis-枚举类型的typeHandler&自定义枚举类型typeHandler

    MyBatis内部提供了两个转化枚举类型的typeHandler给我们使用. org.apache.ibatis.type.EnumTypeHandler 是使用枚举字符串名称作为参数传递的 org. ...

  5. 基于struts2框架-自定义身份证号验证器

    自定义拦截器的步骤: 1.定义一个验证器的类: > 自定义的验证器都需要实现 Validator接口.  > 可以选择继承 ValidatorSupport 或 FieldValidato ...

  6. 自定义数据类型 C++ 结构体类型 共同体类型 枚举类型 类类型{}

    一.结构体类型 结构体类型,共用体类型,枚举类型,类类型等统称为自定义类型(user-defined-type,UDT). 结构体相当于其他高级语言中的记录(record);例如: struct St ...

  7. 自定义fastjson对枚举类型的序列化及反序列化过程

    通常,fastjson在序列化及反序列化枚举时,一般以下几种策略: 1).根据枚举的name值序列化及反序列化(默认) 2).根据枚举的ordinal序列化及反序列化 3).根据枚举的toString ...

  8. Oval框架如何校验枚举类型的一种思路

    前言: Oval校验框架被广泛集成于各类接口参数校验中, 其方便的注解语法, 易读性和扩展性. 几乎成了java后端服务代码的标配. 有人会很疑惑, 都已经是枚举类型了, 还需要校验吗? 其实这边更确 ...

  9. 【转】Java基础笔记 – 枚举类型的使用介绍和静态导入--不错

    原文网址:http://www.itzhai.com/java-based-notes-introduction-and-use-of-an-enumeration-type-static-impor ...

随机推荐

  1. 硕盟type-c转接头|四合一多功能扩展坞

    硕盟SM-T54是一款 TYPE C转HDMI+VGA+USB3.0+PD3.0四合一多功能扩展坞,支持四口同时使用,您可以将含有USB 3.1协议的电脑主机,通过此产品连接到具有HDMI或VGA的显 ...

  2. SpringBoot异步使用@Async原理及线程池配置

    前言 在实际项目开发中很多业务场景需要使用异步去完成,比如消息通知,日志记录,等非常常用的都可以通过异步去执行,提高效率,那么在Spring框架中应该如何去使用异步呢 使用步骤 完成异步操作一般有两种 ...

  3. rootfs -根文件系统制作

    目录 目录 目录 概述 概念 根文件系统是什么 根文件系统中有什么 根文件系统的形式 Busybox 简介 什么是 linuxrc VFS 简介 Busybox 工具 Busybox 目录结构 Men ...

  4. 完美数java

    完全数(Perfect number),又称完美数或完备数,是一些特殊的自然数.它所有的真因子(即除了自身以外的约数)的和(即因子函数),恰好等于它本身.如果一个数恰好等于它的因子之和,则称该数为&q ...

  5. docker学习笔记(二)--配置镜像加速器

    前提:docker已经安装好 配置过程 进入至阿里云开发中心,https://dev.aliyun.com/,点击管理中心 管理中心中,点击左侧镜像加速器. 修改配置文件,使用加速器,根据我们目前Do ...

  6. Dockerfile自动制作Docker镜像(二)—— 其它常用命令

    Dockerfile自动制作Docker镜像(二)-- 其它常用命令 前言 a. 本文主要为 Docker的视频教程 笔记. b. 环境为 CentOS 7.0 云服务器 c. 上一篇:Dockerf ...

  7. Sentry 监控 - Dashboards 事件数据可视化大屏

    系列 1 分钟快速使用 Docker 上手最新版 Sentry-CLI - 创建版本 快速使用 Docker 上手 Sentry-CLI - 30 秒上手 Source Maps Sentry For ...

  8. 教你用ps如何将图片、文字做出模糊斑驳的作旧效果

    1.对图片的图层执行滤镜-风格化-扩散,选变亮优先. 2.选择画笔工具,在画笔选项里选择喷溅59像素,画笔大小我用的是100px,大家可以根据自己的情况自定义大小,前景设置为黑色. 3.在图章图层上方 ...

  9. django2获取url所有路径

    根据原文链接:https://segmentfault.com/a/1190000017378464,整理 第一步:先导入项目根的urls.py路径 from ceshiproject import ...

  10. [转载]PHP命名规则

    PHP命名规则 引用地址:http://www.cnblogs.com/pengxl/p/3571157.html 就一般约定而言,类.函数和变量的名字应该是能够让代码阅读者能够容易地知道这些代码的作 ...