封装一个优雅的自定义的字典验证器,让API字典参数验证更湿滑

前一篇文章 《巧用Java枚举封装和自定义Function,给前端输出标准的字典》,我们实现了一些字典的标准操作,今天我们来封装一个自定义字典验证器。

创建字典验证注解

我们先提供一个和其他内置验证器一致的 @Dictionary 注解,这个注解需要传入一个实现了 IDictionary 接口的枚举类,然后进行字典校验。

java 复制代码
/**
 * <h1>标记进行字典校验</h1>
 *
 * @author Hamm.cn
 * @apiNote 请注意, 请自行做非空验证, 字典必须实现 {@link IDictionary} 接口
 */
@Constraint(validatedBy = DictionaryAnnotationValidator.class)
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Dictionary {
    /**
     * <h3>错误信息</h3>
     */
    String message() default "不允许的枚举字典值";

    /**
     * <h3>使用的枚举类</h3>
     *
     * @see IDictionary
     */
    Class<? extends IDictionary> value();

    /**
     * <h3>验证组</h3>
     */
    Class<?>[] groups() default {};

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

实现字典验证器

在上面的注解中,我们绑定了一个自定义的验证器 DictionaryAnnotationValidator,我们可以通过继承 ConstraintValidator 类来实现:

java 复制代码
/**
 * <h1>枚举字典验证实现类</h1>
 *
 * @author Hamm.cn
 */
@Component
public class DictionaryAnnotationValidator implements ConstraintValidator<Dictionary, Integer> {
    /**
     * <h3>标记的枚举类</h3>
     */
    private Class<? extends IDictionary> enumClazz = null;

    /**
     * <h3>验证</h3>
     *
     * @param value   验证的值
     * @param context 验证器会话
     * @return 验证结果
     */
    @Contract("null, _ -> true")
    @Override
    public final boolean isValid(Integer value, ConstraintValidatorContext context) {
        if (null == value) {
            return true;
        }
        try {
            DictionaryUtil.getDictionary(enumClazz, value);
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     * <h3>初始化</h3>
     *
     * @param dictionary 字典类
     */
    @Contract(mutates = "this")
    @Override
    public final void initialize(@NotNull Dictionary dictionary) {
        enumClazz = dictionary.value();
    }
}

在上面的方法中,我们调用了上一篇文章中编写的 DictionaryUtil 工具类,通过传入枚举类和字典值,获取字典对象,如果获取失败,则自动抛出异常后交给异常控制器抛出给前端。

查询字典的方法

java 复制代码
/**
 * <h3>查字典</h3>
 *
 * @param enumClass 枚举字典类
 * @param key       枚举字典值
 * @param <D>       [泛型] 字典类型
 * @return 查到的字典
 */
public static <D extends IDictionary> @NotNull D getDictionary(@NotNull Class<D> enumClass, int key) {
    return getDictionary(enumClass, IDictionary::getKey, key);
}

/**
 * <h3>查字典</h3>
 *
 * @param enumClass 枚举字典类
 * @param function  获取指定值的方法
 * @param value     比较的值
 * @param <D>       [泛型] 字典类型
 * @return 查到的字典
 */
public static <D extends IDictionary> @NotNull D getDictionary(
        @NotNull Class<D> enumClass, Function<D, Object> function, Object value
) {
    // 取出所有枚举类型
    D[] objs = enumClass.getEnumConstants();
    try {
        for (D obj : objs) {
            if (Objects.equals(function.apply(obj), value)) {
                return obj;
            }
        }
    } catch (Exception exception) {
        log.error(exception.getMessage(), exception);
    }
    throw new ServiceException(
        "传入的值(" + enumClass.getSimpleName() + "=" + value + ")不在字典可选范围内", 
        getDictionaryList(enumClass)
    );
}

// 省略其他方法

如何使用

我们可以在需要校验的实体类上标记字典注解即可,像这样:

java 复制代码
@Description("物料类型")
@Column(columnDefinition = "bigint UNSIGNED default 1 comment '物料类型'")
@Dictionary(value = MaterialType.class, groups = {WhenAdd.class, WhenUpdate.class})
private Integer materialType;

然后在控制器中绑定这个 group 即可:

java 复制代码
@Description("添加")
@PostMapping("add")
public Json add(@RequestBody @Validated(WhenAdd.class) E source) {
  // 验证过后的 source
}

实现效果

接下来我们看看在提交了错误字典枚举值的异常抛出前端显示如何?

json 复制代码
{
	"code": 500,
	"message": "传入的值(FileCategory=3)不在字典可选范围内",
	"data": [
		{
			"key": 0,
			"label": "临时文件"
		},
		{
			"key": 1,
			"label": "普通文件"
		},
		{
			"key": 1001,
			"label": "头像"
		}
	]
}

嗯,很不错,美滋滋,一看就知道传错了字典值,而且列出了所有的可选字典值,这样前端就可以选择正确字典值提交了。

总结

通过封装一个字典验证器,让字典验证更简单,我们只需要在实体类上标记字典注解,然后在控制器中绑定对应的验证组即可。

完整代码可以参考开源项目:

github.com/HammCn/AirP...

又水了一篇,美滋滋。

Bye~

相关推荐
没明白白11 分钟前
插入排序:一种简单而直观的排序算法
java·算法·排序算法
小猪咪piggy14 分钟前
【数据结构】(12) 反射、枚举、lambda 表达式
java·开发语言·数据结构
web1478621072317 分钟前
数据库系统架构与DBMS功能探微:现代信息时代数据管理的关键
java·开发语言·数据库
wolf犭良17 分钟前
21.《SpringBoot 异步编程@Async与CompletableFuture》
java·数据库·spring
程序员南飞31 分钟前
算法-数据结构-图的构建(邻接矩阵表示)
java·数据结构·算法·职场和发展
舰长1151 小时前
安装可视化jar包部署平台JarManage
java·jar
天上掉下来个程小白1 小时前
登录-10.Filter-登录校验过滤器
spring boot·后端·spring·filter·登录校验
抹除不掉的轻狂丶1 小时前
JVM生产环境问题定位与解决实战(三):揭秘Java飞行记录器(JFR)的强大功能
java·开发语言·jvm
合方圆~小文1 小时前
跨境宠物摄像头是一种专为宠物主人设计的智能设备
java·数据库·人工智能·扩展屏应用开发
SomeB1oody2 小时前
【Rust中级教程】2.8. API设计原则之灵活性(flexible) Pt.4:显式析构函数的问题及3种解决方案
开发语言·后端·性能优化·rust