对于 Java 程序员来说,接口的参数校验一般是通过 @NotNull 、 @NotBlank 、 @Size(min = 0, max = 255) 等这些注解来校验的。
但是有一些情况下,这些注解无法满足校验的需求,比如下面这段代码:
public class Test {
/**
* 状态,值必须为枚举类 Status 中的 code
*/
Integer status;
/**
* 是否开启语音提示
*/
Boolean enableVoice;
/**
* 语音提示内容
*/
String voiceContent;
}
这里涉及到两种参数校验是现有的注解无法满足的:
这里我只是进行举例,我主要是想收集一下大家遇到的类型情况,然后我想做一个组件,能够尽可能的解决这些问题。
其实我已经有一个这样的组件了,但是担心自己考虑到的情况还不太够,所以想收集一下大家的建议,之后完善一下然后开源出来给大家使用。
我目前实现的这套东西是基于 spel + 注解 的,本来想到V站问一下有需求的场景,没想到得到了这么多关于这个组件的评价😅,目前的情况是,我发布这个帖子到现在以来还没有收集到一个有用的用例场景😅。
我实现的这套东西在使用上绝对是没毛病的,大大的提升了便利,举个例子:
public class TestParamVo {
/**
* 状态,值必须为枚举类 StatusEnum 中的 code
*/
@SpelAssert(" StatusEnum.getCode(#this.status) != null ") // 当表达式成立时校验通过
Integer status;
/**
* 是否开启语音提示
*/
Boolean enableVoice;
/**
* 语音提示内容
*/
@SpelNotNull(" enableVoice == true ") // 当表达式成立时,注解标记的值必须不为null
String voiceContent;
}
这是我目前实现的东西,由于是基于 Spel 的,所以可用性很高,如果你需要,甚至可以直接调用 spring bean(当然我不建议这样做),比如这样:
public class TestParamVo {
/**
* 用户id,必须是有效的用户
*/
@SpelAssert(" userService.getById(#this.userId) != null ") // 当表达式成立时校验通过
Integer userId;
}
这只是部分简单的demo,我实现的东西比这个更完善。另外,这套组件确实基于 ConstraintValidator,但远不是简单的自定义一个 Validator 这么简单的,因为 ConstraintValidator 中只能拿到当前标记的字段值,无法获取到上下文的其他字段(enableVoice那个场景解决不了)。
希望大家尽可能提供用例,而不是劝退我,谢谢。
1
gitdoit 237 天前
你是否在寻找 javax.validation.constraints.AssertTrue
|
2
liaojl 237 天前 via iPhone
复杂的参数校验直接 service 里写了,上下文关联的,有些还要查数据库校验的,很难做到一个组件通吃的。
|
5
cheng6563 237 天前
简单解决下常用问题就行了,别搞太复杂。
你是可以搞个解决所有问题的 DSL 来做参数验证,但到了那阶段还不如直接敲程序解决了 |
6
JoeDH 237 天前
之前碰到一个,前端传入状态值的,我想用判断状态值是否在已有枚举中,或者在集合里面,我找了下好像没有支持这个情形的注解?
|
7
walle1530 237 天前
你是否在寻找 javax.validation.ConstraintValidator
|
8
sticki OP |
9
sticki OP @walle1530 ConstraintValidator 可以解决一部分问题,但不够通用,比如我上面举例的第 2 点,enable 的那个,用 ConstraintValidator 是无法解决的
|
10
walle1530 237 天前
@sticki #9 理论上来说应该是可以的,定义注解的目标参数和依赖参数,然后定义 Validator 来写检查逻辑,但除非你有大量此类的依赖参数需要检查,如果只是一两个的话,这样更麻烦了。
|
11
pxcking 237 天前
public class NotIncludeNullValidator implements ConstraintValidator<NotIncludeNull, Collection<?>> {
@Override public void initialize(NotIncludeNull constraintAnnotation) { } @Override public boolean isValid(Collection<?> value, ConstraintValidatorContext context) { // 不校验空集合 if (CollectionUtils.isEmpty(value)) { return true; } for (Object next : value) { if (next == null) { return false; } } return true; } } |
12
Ashe007 237 天前 via iPhone
没必要,我参数检验选择使用 spring 自带的断言 Assert ,写在参数类中,通过参数对象调用即可
不明白为啥那么多写 jar 包的,特别是写一些垃圾代码的。让人看着就想喷 |
13
Ashe007 237 天前 via iPhone
@Ashe007 比如 Java bean 属性用基本类型,工具类是某个 apache 工具类套壳,或者内部本身存在 bug 或非常不合理的逻辑
|
14
chihiro2014 237 天前 1
|
15
RedBeanIce 237 天前
加油,
需要查库等的复杂逻辑,我一般写在代码中。 |
16
mmdsun 237 天前 via iPhone
这个校验注解可以自定义,加类上都可以
|
17
yosoroAida 237 天前
你这可以在参数里面写个方法,并加上 @NotNull 注解 ,参考下我这例子
```java @Setter class ExampleParam{ private Integer code; @NotNull(message = "你的业务逻辑提示") public ExampleEnum getCodeEnum() { // 根据 code 去获取 ExampleEnum ,然后返回 return ExampleEnum.getByValue(code); } } @AllArgsConstructor @Getter enum ExampleEnum{ ; private final Integer code; public static ExampleEnum getByValue(Integer value){ for (ExampleEnum exampleEnum : ExampleEnum.values()) { if(exampleEnum.getCode().equals(value)){ return exampleEnum; } } return null; } } ``` |
18
yosoroAida 237 天前
@yosoroAida
这样的话,在 Validate 框架去做校验的时候,也会去校验上面个的 getCodeEnum() 是否返回为空,如果返回为空的话,就会把 @NotNull 的 message 的信息给返回回去,这样就免去你在业务逻辑里面做参数校验了。 这样也体现了面向对象编程的优势对吧? |
19
JackCh3ng 237 天前
如果 status 是枚举值,为什么不把 Integer 直接换成枚举类呢?有什么问题吗?
|
20
halov 237 天前
不是可以增加自定义的验证注解吗 ,通过 aop 的方式 复杂的业务验证也可以实现
|
21
jov1 237 天前
也会有类似问题,通用的用提供的注解声明,这种情况判定来动态处理验证的,我目前放在业务实现里面校验处理,因为可能还需要对数据处理,比如 enableVoice 不等于 true ,如果前端传入了 voiceContent ,后端其实还类似需要清空这个值,那么顺手可以把校验 enableVoice=true 时 voiceContent 的非空校验做了,
类似于 if (BooleanUtils.isTrue(xx.getEnableVoice()) { } |
22
jov1 237 天前
if (BooleanUtils.isTrue(xx.getEnableVoice()) {
// 校验 voiceContent 是否为空 } else { xx.setVoiceContent(null); } 应该也可以实现 ConstraintValidator 来自定义校验规则,拓展注解之类的,只是目前是类似这样处理的 |
23
unclevv 237 天前
只有我一个人觉得 Boolean enableVoice 定义不规范吗,Boolean voiceSwitch 与 String voiceContent 对应,作为变量,enableVoice 应该是用作方法名
|
24
layxy 237 天前
1.自定义校验注解和校验器
2.建议手动校验吧,自动校验也可以实现,但是把业务判断逻辑放到配置中感觉不是太好 |
25
KongLiu 237 天前
自定义验证+正则
|
26
liuzhaowei55 236 天前 via Android
可以看看 laravel 的 validation 设计,完爆其他所有语言,框架的校验
目前比较需要的就是你提到的这个校验入参值是否在给定可选值中 |
27
Karte 236 天前
对于枚举的验证做法:
1. 硬代码, 通过 Assert 相关断言判断 2. 通过 @Min, @Max 设定枚举边界. 3. 通过实现 `ConstraintValidator` 对类进行校验. 4. 通过代理 + 反射的方式添加自定义处理注解, 然后通过定义 `spel` 表达式确认是否校验通过. ```java @FieldValid(spel = "#p1.field != 0") ``` 用硬代码虽然会略显丑陋, 但是在新增枚举时可以不用改动代码. 而 @Min, @Max 在枚举修改后需要同步修改, 如果没有相关注释说明会存在隐性 BUG. 使用 `ConstraintValidator` 相对比较好, 因为能够自定义实现校验逻辑. 但是验证范围只能为对应类, 如果不做继承的话 无法实现复用 代理 + 反射会有略微性能损耗, 而 `spel` 表达式需要额外学习, 且有点耗费性能 (通过反射获取数据). 在多参数时需要在编译时增加 `-parameters` 否则参数名无法写入, `spel` 就无法获取到对应的对象, 校验就失败了. |
29
sticki OP 我才添加完附言,就没人回复了,沉底了🤡🤡
|
30
Aresxue 230 天前 1
|