优雅的参数校验
添加依赖
如果使用的是Springboot就不需要手动添加依赖了。Springboot已经依赖了。
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
参数检验的使用
注解名 | 含义 |
---|---|
AssertFalse | 带注释的元素必须为false |
AssertTrue | 带注释的元素必须为true |
DecimalMax | 带注释的元素必须是一个数字,其值必须小于或等于指定的最大值 |
DecimalMin | 带注释的元素必须是一个数字,其值必须大于或等于指定的最小值 |
Digits | 字段必须为数值,且正数部分不能超过 i 位,小数部分不能超过 j 位,null 元素被视为有效。 |
所注解的元素需满足Email格式 | |
Future | 带注释的元素必须是将来的日期 |
FutureOrPresent | 字段必须为未来的时间或当前的时间 |
Past | 所注解的元素必须是某个过去的日期 |
PastOrPresent | 字段必须为过去的时间或当前的时间从 |
Max | 带注释的元素必须是一个数字,其值必须小于或等于指定的最大值 |
Min | 带注释的元素必须是一个数字,其值必须大于或等于指定的最小值 |
NotBlank | 所注解的元素值有内容 |
NotEmpty | 字段不能为 null 且不能为空,可以作用于字符串,其长度不能为 0,可作用于 Array、Collection、Map,其大小不能为 0 |
NotNull | 所注解的元素值不能为null |
Null | 所注解的元素值为null |
Pattern | 所注解的元素必须满足给定的正则表达式 |
Positive | 字段必须为正数,即数值大于 0 |
PositiveOrZero | 字段必须为正数或 0,即数值大于等于 0 |
Negative | 字段必须为负数,即数值小于 0 |
NegativeOrZero | 字段必须为负数或 0,即数值小于等于 0 |
Size | 所注解的元素必须是String、集合或数组,且长度大小需保证在给定范围之内 |
- 实体类增加属性注解
@Size(max= 64, message = "长度不能超过64")
private String orderNo;
@NotBlank(message = "类型不能为空")
private String orderType;
@Past(message = "创建时间在当前时间之前才可成功")
private Date createDate;
@Future(message = "更新时间在当前时间之后才可成功")
private Date updateDate;
@Email
private String email;
@Pattern(regexp = "^(\\d{6})(\\d{4})(\\d{2})(\\d{2})(\\d{3})([0-9]|X)$",message = "身份证格式不正确")
private String idNum;
- 方法使用实体类入参的时候增加校验注解
public Rs save(@RequestBody @Validated MiTestEntity miTest) {
return Rs.ok();
}
public Rs update(@RequestBody @Valid MiTestEntity miTest) {
return Rs.ok();
}
- 使用全局异常拦截错误的字段校验
@RestControllerAdvice
public class BindExceptionHandler {
@ExceptionHandler(Exception.class)
public Rs handleException(Exception e) {
Rs r = new Rs();
r.put("code" , 500);
r.put("msg" , e.getMessage());
return r;
}
/**
* 方法参数校验
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Rs handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
return Rs.error(e.getBindingResult().getFieldError().getDefaultMessage());
}
}
@NotNull, @NotEmpty和@NotBlank之间的区别是什么?
-
@NotEmpty
- 不能是null
- 不能是空字符
- 集合框架中的元素不能为空
-
@NotNull
- 被修饰元素不能为null,可用在基本类型上
-
@NotBlank
- String不能是null,且去除两端空白字符后的长度0
-
@NotEmpty或者@NotBlank不可以用在基本数据类型上,会报错
- rg.springframework.web.util.NestedServletException: Request processing failed; nested exception is javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint 'javax.validation.constraints.NotBlank' validating type 'java.lang.Long'. Check configuration for 'offset'
@validated和@valid的区别
- @Valid:标准JSR-303规范的标记型注解,用来标记验证属性和方法返回值,进行级联和递归校验。
- @Validated:Spring的注解,是标准JSR-303的一个变种(补充),提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制
- 在Controller中校验方法参数时,使用@Valid和@Validated并无特殊差异,但是@Validated不可以嵌套校验,
public class A{
@NotNull(message = "id不能为空")
@Min(value = 1, message = "id必须为正整数")
private Long id;
@NotNull(message = "B不能为空")
@Size(min = 1, message = "至少要有一个属性")
private List<B> bList;
}
public class B{
@NotNull(message = "id不能为空")
@Min(value = 1, message = "id必须为正整数")
private Long id;
@NotBlank(message = "name不能为空")
private String name;
}
//这样的情况下如果使用@Validated校验A的参数,那么bList泛型里面的属性则不会校验成功
//需要在private List<B> bList;上面加上@Valid来支持嵌套验证
- @Validated注解可以用于类级别,用于支持Spring进行方法级别的参数校验。@Valid可以用在属性级别约束,用来表示级联校验
- @Validated只能用在类、方法和参数上,而@Valid可用于方法、字段、构造器和参数上
自定义注解验证
某些业务场景下,上面的注解未必可以提供很好的校验,validation也为我们提供了自定义注解参数校验。
- 注解
@Documented
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {StringLengthValidImpl.class})
public @interface StringLength {
/**
* 在注解没有显示申明,则min值默认是 0
*/
int min() default 0;
/**
* 在注解没有显示申明,则max值默认是18
*
* @return
*/
int max() default 18;
/**
* 错误信息
*/
String message() default "";
/**
* 分组
*/
Class<?>[] groups() default {};
//TODO 暂时不知道是干嘛的 看接口的注释是约束注解的负载(可用来保存一些数据)
Class<? extends Payload>[] payload() default {};
/**
* 定义验证集合,可以为属性配置多套的验证规则
*/
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface List {
StringLength[] value();
}
}
- 校验实现类
//<注解类,注解作用的参数类型(泛型)>
public class StringLengthValidImpl implements ConstraintValidator<StringLength, String> {
private int min;
private int max;
private String message;
/**
* @Description: 初始化参数
* @param: integerValid
*/
@Override
public void initialize(StringLength integerValid) {
max = integerValid.max();
min = integerValid.min();
message = integerValid.message();
}
/**
* @param: value 参数
* @param: constraintValidatorContext 在应用给定的约束验证器时提供上下文数据和操作
* @return: boolean
*/
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
//禁止默认消息返回
context.disableDefaultConstraintViolation();
if (null == value) {
context.buildConstraintViolationWithTemplate(message + ":参数值为空").addConstraintViolation();
return false;
} else if (value.length() > max) {
context.buildConstraintViolationWithTemplate(message + ":不能超过" + max).addConstraintViolation();
return false;
} else if (value.length() <= min) {
context.buildConstraintViolationWithTemplate(message + ":不能小于" + min).addConstraintViolation();
return false;
}
return true;
}
}
- 分组校验
//用于新增校验
public interface save {
}
//用于修改校验
public interface update {
}
- 参数校验
@Data
public class TestEntity {
@StringLength.List({
@StringLength(groups = {save.class}, message = "name",max = 10),
@StringLength(groups = {update.class}, message = "name",max = 5)
})
private String name;
}
//不可以超过10个字符
@PostMapping("add")
public String add(@RequestBody @Validated({save.class}) TestEntity testEntity) {
return JSON.toJSONString(testEntity);
}
//不可以超过5个字符
@PostMapping("update")
public String update(@RequestBody @Validated({update.class}) TestEntity testEntity) {
return JSON.toJSONString(testEntity);
}
//没有限制
@PostMapping("save")
public String save(@RequestBody @Validated TestEntity testEntity) {
return JSON.toJSONString(testEntity);
}