- Spring Validation概述
- 1、基本使用
- 1.1、首先以 新增用户信息 为示例来测试第一类
- 1.1.1、正常测试
- 1.1.2、长度测试
- 1.1.3、@NotNull 测试
- 1.2、第二类以 获取用户信息 @RequestParam / @PathVariable 测试
- 2、进阶使用
- 2.1、分组校验
- 2.2、嵌套校验
- 2.3 、集合测试
- 2.4 、自定义校验
- 2.5 编程式校验
- 3、fail-fast
- 4、更多注解
如果你有前端传参校验的需求,而又不想手动写if去校验的话,本文应该可以帮到你 :) Spring Validation概述
Spring Validation是对hibernate validation的二次封装,用于支持spring mvc参数自动校验。
1、基本使用如果spring-boot版本小于2.3.x,spring-boot-starter-web会自动传入hibernate-validator依赖。如果spring-boot版本大于2.3.x,则需手动导入依赖
版本说明:springboot 2.6.1
maven依赖
org.springframework.boot spring-boot-starter-web org.hibernate hibernate-validator 6.0.1.Final org.projectlombok lombok true
分两类参数传递进行测试
一般来说
1、POST、PUT请求,使用@RequestBody传递参数;
2、GET请求,使用@RequestParam / @PathVariable传递参数。
1.1、首先以 新增用户信息 为示例来测试第一类新建dto
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotNull;
@Data
public class User {
@NotNull
@Length(min = 2, max = 10)
private String username;
@NotNull
@Length(min = 2, max = 10)
private String pwd;
private String id;
}
新建接口测试
import com.example.springvalidation.dto.User;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@PostMapping("save")
public String saveUser(@RequestBody @Validated User user){
return "validation success";
}
}
1.1.1、正常测试
1.1.2、长度测试
或短或长皆会由spring转为400
同时控制台,会打印消息 “长度需要在2和10之间”
2021-12-17 13:26:13.950 WARN 19524 --- [nio-8080-exec-8] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public java.lang.String com.example.springvalidation.controller.TestController.saveUser(com.example.springvalidation.dto.User): [Field error in object 'user' on field 'username': rejected value [1]; codes [Length.user.username,Length.username,Length.java.lang.String,Length]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.username,username]; arguments []; default message [username],10,2]; default message [长度需要在2和10之间]] ]1.1.3、@NotNull 测试
由结果看, null会进行拦截,美中不足的是 ""会以长度不足的分支去拦截
自定义消息提示
@NotNull(message="null自定义提示") @Length(min = 2, max = 10, message="长度自定义提示")1.2、第二类以 获取用户信息 @RequestParam / @PathVariable 测试
新增接口,注意类上面要加@Validated注解
import com.example.springvalidation.dto.User;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.Min;
@RestController
@Validated
public class TestController {
@PostMapping("save")
public String saveUser(@RequestBody @Validated User user) {
return "validation success";
}
@GetMapping("{id}")
public String getUser(@PathVariable("id") @Min(0L) int id) {
return "validation success";
}
@GetMapping("get")
public String getUser2(@RequestParam("id") @Min(0L) @NotNull int id) {
return "validation success 2";
}
}
当出现不符合要求的id时
getUser2 接口测试,当分别访问
localhost:8080/get?id=null localhost:8080/get?id="" localhost:8080/get?id=-1
结果分别为
dto修改如下
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotNull;
@Data
public class User {
@NotNull(message="null自定义提示")
@Length(min = 2, max = 10, message="长度自定义提示")
private String username;
@NotNull(groups = {Save.class, Update.class})
@Length(min = 2, max = 10, groups = {Save.class, Update.class})
private String pwd;
private String id;
public interface Save { }
public interface Update { }
}
接口修改如下:
@PostMapping("save")
public String saveUser(@RequestBody @Validated(User.Save.class) User user) {
return "validation success";
}
自测~
2.2、嵌套校验当一个对象携带其他对象内的参数也需要校验的时候,可以使用 @Valid 来进行嵌套,可以重新定义Authority 类的校验规则,也可重新建一个类,进行校验规则复用
dto修改如下
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.Valid;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
@Data
public class User {
@NotNull(message="null自定义提示")
@Length(min = 2, max = 10, message="长度自定义提示")
private String username;
@NotNull(groups = {Save.class, Update.class})
@Length(min = 2, max = 10, groups = {Save.class, Update.class})
private String pwd;
private String id;
@NotNull(groups = {Save.class, Update.class})
@Valid
private Authority authority;
@Data
public static class Authority {
@Min(value = 1, groups = Update.class)
private Long id;
@NotNull(groups = {Save.class, Update.class})
@Length(min = 2, max = 10, groups = {Save.class, Update.class})
private String name;
//更多...
}
public interface Save { }
public interface Update { }
}
这样 对于 User 内的所有字段都会校验,包括 Authority 内部定义的规则
细心的会发现了一个问题
@Valid和@Validated区别
| 区别 | @Valid | @Validated |
|---|---|---|
| 提供者 | JSR-303 规范 | Spring |
| 是否支持分组 | 不支持 | 支持 |
| 标注位置 | METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE | TYPE, METHOD, PARAMETER |
| 嵌套校验 | 支持 | 不支持 |
当前端直接传递json字符串来时,可自定义集合测试
新增dto,ValidationList
import lombok.experimental.Delegate; import javax.validation.Valid; import java.util.ArrayList; import java.util.List; public class ValidationListimplements List { @Delegate @Valid public List list = new ArrayList<>(); @Override public String toString() { return list.toString(); } }
新增接口
@PostMapping("/saveList")
public String saveList(@RequestBody @Validated(User.Save.class) ValidationList userList) {
return "validation success";
}
这样整个json里面的参数,就会按照我们定义的规则(User)进行校验,按照规则,各位看官可根据规则,键入不合符规则数据自行测试
实现 ConstraintValidator 接口,编写约束校验器,即定义校验规则,这里我们验证密码,由数字或者a-d的字母组成,2-10长度
import com.example.springvalidation.annotation.SjtValidation; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.util.regex.Matcher; import java.util.regex.Pattern; public class SjtValidator implements ConstraintValidator{ //自定义校验规则 private static final Pattern PATTERN = Pattern.compile("^[a-d\d]{2,10}$"); @Override public boolean isValid(String value, ConstraintValidatorContext context) { if (value != null) { Matcher matcher = PATTERN.matcher(value); return matcher.find(); } return true; } }
新增注解
import com.example.springvalidation.validator.SjtValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})@Retention(RUNTIME)
@documented
@Constraint(validatedBy = {SjtValidator.class})
public @interface SjtValidation {
//模仿现有的注解,实现默认的提示消息返回
String message() default "密码格式错误";
//支持分组
Class>[] groups() default {};
Class extends Payload>[] payload() default {};
}
为了干净,新增dto,User2,使用 @SjtValidation
import com.example.springvalidation.annotation.SjtValidation;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Data
public class User2 {
private String id;
private String username;
@NotNull(groups = {User.Save.class, User.Update.class})
@SjtValidation
private String pwd;
}
新增接口
@PostMapping("save2")
public String saveUser2(@RequestBody @Validated User2 user2) {
return "validation success";
}
测试即可,可以发现,我们的密码需要按照我们的规则来进行传递,各位看官可在此基础上自由发挥,自由验证,自由组合前面的各种校验测试
2.5 编程式校验注入javax.validation.Validator,调用api进行校验
@Resource
private javax.validation.Validator validator;
@PostMapping("/save3")
public String saveUser3(@RequestBody User user) {
Set> validate = validator.validate(user, User.Save.class);
if (validate.isEmpty()) {
return "validation failed";
} else {
for (ConstraintViolation constraintViolation : validate) {
System.out.println(constraintViolation);
}
return "validation success";
}
}
这里不在进行测试,我个人不是特别喜欢这种方式。。
3、fail-fastSpring Validation默认会校验完所有字段,然后才抛出异常,启动此机制,碰到错误的校验直接返回,后面的字段不在校验
启动类添加bean实现快速失败
import org.hibernate.validator.Hibernatevalidator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
@SpringBootApplication
public class SpringvalidationApplication {
//校验快速失败
@Bean
public Validator validator() {
ValidatorFactory validatorFactory = Validation.byProvider(Hibernatevalidator.class)
.configure()
.failFast(true)
.buildValidatorFactory();
return validatorFactory.getValidator();
}
public static void main(String[] args) {
SpringApplication.run(SpringvalidationApplication.class, args);
}
}
4、更多注解
更多的内容需要各位看官自行去挖掘啦~



