前言:
在spring-boot学习中,遇到了有限定条件的值,前端传来的数据需要有格式限制,例如url,邮箱等限制,为了避免前端工作量太大,于是在后端传递数据是进行校验数据。
为了满足要求,使数据合法我们要对数据进行检验。
validated
spring-boot中可以用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。结尾有常用的validated注释方法。
后台校验(服务端校验):
- Controller层:校验前台页面提交过来的参数的合法性
- Service层:校验service接口中使用的参数
- DAO层:一般不校验
正常使用:
本次使用中使用的Controller层的校验:
@Data
public class Article {
private Integer id;//主键ID
@NotEmpty
@Pattern(regexp = "^\\S{1,10}$")
private String title;//文章标题
@NotEmpty
private String content;//文章内容
@NotEmpty
@URL
private String coverImg;//封面图像
@State
private String state;//发布状态 已发布|草稿
@NotNull
private Integer categoryId;//文章分类id
private Integer createUser;//创建人ID
private LocalDateTime createTime;//创建时间
private LocalDateTime updateTime;//更新时间
}
使用@Validated
注解
@Validated
可以应用在控制器类上,也可以应用在方法级别,用来启用Bean Validation
@PostMapping
public Result add(@RequestBody @Validated Article article){
articleService.add(article);
return Result.success();
}
使用多个注释,对数据加以判断。其中有一个 @State为自定义注释,我们后续再说。
发现问题:
@Data
public class Category {
@NotNull(groups = Update.class)
private Integer id;//主键ID
@NotEmpty
private String categoryName;//分类名称
@NotEmpty
private String categoryAlias;//分类别名
private Integer createUser;//创建人ID
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;//创建时间
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;//更新时间
public interface Add extends Default {
}
public interface Update extends Default {
}
}
在使用实体Category时,发现id在添加和修改时的判断条件不同,由于前文在jwt令牌中封存了id,使用ThreadLocal传递id,而上文提到判断参数实在Contrioller层判断,而id是在service层传递的:
@Override
public Category findById(Integer id) {
Category c = categoryMapper.findById(id);
return c;
}
导致在测试时出现该参数不能为空的错误,于是就在该实体类进行分组
Validated分组:
如果说某个校验项没有指定分组,默认属于Default分组,分组之间可以继承 A extend B A中有b的所有校验项
@NotNull(groups = Update.class)
private Integer id;//主键ID
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;//创建时间
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;//更新时间
public interface Add extends Default {
}
public interface Update extends Default {
}
这样使得id在添加时必须存在,而修改时后续传参id为空也不影响。
自定义注释
在接口文档中,对state的注释是只能为已发布或者草稿,在现成的validated注解中,显然没有可以达成的,也可以创建一个枚举结构,使传入数据为枚举中其一,但是这里使用自定义注释的方法
自定义:
@Documented
@Target({ElementType.FIELD})
@Constraint(validatedBy = {StateValidation.class})
@Retention(RetentionPolicy.RUNTIME)
public @interface State {
//提供校验失败后的信息
String message() default "state参数的值只能说草稿或已发布";
//指定分组
Class<?>[] groups() default {};
//负载,获取State注解的附加信息
Class<? extends Payload>[] payload() default {};
}
元注解,可被抽取 :@Documented
元注解,可用于那些地方: @Target({ElementType.FIELD})
指定通过校验规则的类 :@Constraint(validatedBy = {StateValidation.class})
元注解,在那些阶段被保留,运行阶段:@Retention(RetentionPolicy.RUNTIME)
对应这些注解,我们可以从其中现成的注解中,进入源码照猫画虎即可:
以@NotEmpty为例
@Documented
@Constraint(
validatedBy = {}
)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(List.class)
public @interface NotEmpty {
String message() default "{jakarta.validation.constraints.NotEmpty.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface List {
NotEmpty[] value();
}
}
完成后输入数据只能为已发布或者草稿,至于前段页面是输入框,还是选择框,这些就由前端程序员决定了。
常用的validated注解
// JSR提供的校验注解:
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max=, min=) 被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式
// Hibernate Validator提供的校验注解:
@NotBlank(message =) 验证字符串非null,且长度必须大于0
@Email 被注释的元素必须是电子邮箱地址
@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内