Validation 作用
在开发过程中经常会用到参数校验的过程,例如在用户注册过程中需要校验用户注册的信息是否合法,如果用 if else语句来判断的话,当判断条件很多时会很麻烦, 而 Validation 依赖就可以很好的解决这个问题。
使用
导入依赖
在 pom.xml 文件中导入 validation 依赖
<dependencies>
<!--validation 依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>
在类中加入 @Validated 注解
示例一
@Validated
@RestController
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/register")
public Result register(@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$") String password){
// 查看用户名是否被占用
User user = userService.findByUsername(username);
if (null == user) {
userService.register(username, password);
}
// 已被占用
else{
return Result.error("用户名已被占用");
}
return Result.success();
}
}
在这段代码中,在注册时要判断用户名和密码的长度在5-16范围内,若用寻常的方式则会像下面这样比较繁琐
@PostMapping("/register")
public Result register(String username, String password){
if (username!=null && username.length()>=5 && username.length()<=16 &&
password!=null && password.length()>=5 && password.length()<=16){
//查询用户
User u = userService.findByUserName(username);
if (u==null){
//没有占用
//注册
userService.register(username,password);
return Result.success();
}else{
//占用
return Result.error("用户名已被占用");
}
}else{
return Result.error("参数不合法");
}
}
示例二
当前端出入的是一个实体类,而我们需要对类中的某些字段进行参数校验时,就无法用上述提到的 @Pattern 注解了,可以找到对应的实体类,在实体类中加入相对应的注解,例如这里的非空注解 @NotEmpty 使用下面的方式。
import com.fasterxml.jackson.annotation.JsonFormat;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.groups.Default;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class Category {
@NotNull
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;//更新时间
}
然后在 Controller 层加入 @Validation 注解使校验条件生效
@RestController
@RequestMapping("/category")
public class CategoryController {
@Autowired
private CategoryService categoryService;
@PostMapping
public Result add(@RequestBody @Validated Category category){
categoryService.add(category);
return Result.success();
}
}
校验分组
在开发过程中会碰到这种情况:当添加用户时,id 是由后端自动生成的,不需要校验 id 是否为空,而在修改用户 id 时则需要校验。如果在 User 实体类上添加 @NotNull 注解,则添加和修改用户的业务会冲突,这时候就需要用到分组校验。
例如在示例二的代码中,添加分组校验后的结果为
import com.fasterxml.jackson.annotation.JsonFormat;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.groups.Default;
import lombok.Data;
import java.time.LocalDateTime;
@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;//更新时间
//如果说某个校验项没有指定分组,默认属于Default分组
//添加了指定分组的校验只属于自定义分组,不属于 Default 分组
//分组之间可以继承, A extends B 那么A中拥有B中所有的校验项
public interface Add extends Default {
}
public interface Update extends Default{
}
}
对应 Controller 代码如下,在增加和修改文章信息的方法中指明要使用哪个分组校验规则。
@RestController
@RequestMapping("/category")
public class CategoryController {
@Autowired
private CategoryService categoryService;
// 新增文章信息
@PostMapping
public Result add(@RequestBody @Validated(value = Category.Add.class) Category category){
categoryService.add(category);
return Result.success();
}
// 修改文章信息
@PutMapping
public Result update(@RequestBody @Validated(value = Category.Update.class) Category category){
categoryService.update(category);
return Result.success();
}
}
自定义校验规则
在开发过程中可能会碰到 @Validated 中自带的参数校验无法满足开发场景,此时就需要用到自定义的参数校验。
例如这里需要对 Article 类中的 state 属性进行校验,使其值只能是 “草稿” 或“已发布”,这里采用了自定义校验规则@State。 其他属性可用 @Validated 自带的校验规则。
import com.itheima.anno.State;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import lombok.Data;
import org.hibernate.validator.constraints.URL;
import java.time.LocalDateTime;
@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;//更新时间
}
定义校验规则步骤为两步:
1.创建自定义注解, 这里取名为State
@Documented//元注解,表示自定义的State注解是可以抽取到帮助文档里面的
@Target({ FIELD})//元注解,标识自定义的State注解可以用在哪些地方,因为这里只用在属性上,所以只写了FIELD
@Retention(RUNTIME)//元注解,标识该注解将来会在那个阶段保留,这里标识是运行时阶段
@Constraint(validatedBy = { StateValidation.class})//指定提供校验规则的类
public @interface State {
//提供校验失败后的提示信息
String message() default "state参数的值只能是已发布或者草稿";
//指定分组
Class<?>[] groups() default { };
//负载 获取到State注解的附加信息
Class<? extends Payload>[] payload() default { };
}
在自定义注解时可以参考官方注解定义方式
@Documented
@Constraint(
validatedBy = {}
)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(NotEmpty.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();
}
}
2.创建实现自定义注解 State 的类
// ConstraintValidator<给哪个注解提供校验规则,校验的数据类型>
public class StateValidation implements ConstraintValidator<State,String> {
/**
*
* @param value 将来要校验的数据
* @param context context in which the constraint is evaluated
*
* @return 如果返回false,则校验不通过,如果返回true,则校验通过
*/
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
//提供校验规则
if (value == null){
return false;
}
if (value.equals("已发布") || value.equals("草稿")){
return true;
}
return false;
}
}