1. 参数校验
spring-boot-starter-web包里面有hibernate-validator包,不需要引用hibernate validator依赖。
2. hibernate validator校验demo
1. 导入包
import org.hibernate.validator.constraints.NotBlank;
import javax.validation.constraints.AssertFalse;
import javax.validation.constraints.Pattern;
2. demo
@Getter
@Setter
@NoArgsConstructor
public class DemoModel {
@NotBlank(message="用户名不能为空")
private String userName;
@NotBlank(message="年龄不能为空")
@Pattern(regexp="^[0-9]{1,2}$",message="年龄不正确")
private String age;
@AssertFalse(message = "必须为false")
private Boolean isFalse;
/**
* 如果是空,则不校验,如果不为空,则校验
*/
@Pattern(regexp="^[0-9]{4}-[0-9]{2}-[0-9]{2}$",message="出生日期格式不正确")
private String birthday;
}
3. 结果返回
@RequestMapping("/demo2")
public void demo2(@RequestBody @Valid DemoModel demo, BindingResult result){
if(result.hasErrors()){
for (ObjectError error : result.getAllErrors()) {
System.out.println(error.getDefaultMessage());
}
}
}
4. 传入参数
{"userName":"dd","age":120,"isFalse":true,"birthday":"21010-21-12"}
5. 输出结果
出生日期格式不正确
必须为false
年龄不正确
3. hibernate的校验模式
1. 普通模式(默认为该模式)
会校验所有属性,然后返回所有的验证失败信息。
2. 快速失败返回模式
只要有一个校验失败则返回。
3. 设置方式
// failFast: true 快速失败返回模式,false 普通模式
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
.configure()
.failFast( true )
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
// hibernate.validator.fail_fast: true 快速失败返回模式,false 普通模式
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
.configure()
.addProperty( "hibernate.validator.fail_fast", "true" )
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
4. hibernate的校验
配置hibernate Validator为快速返回模式: @Configuration
public class ValidatorConfiguration {
@Bean
public Validator validator(){
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
.configure()
.addProperty( "hibernate.validator.fail_fast", "true" )
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
return validator;
}
}
1. 请求参数校验
验证请求参数时,在 @RequestBody DemoModel demo 之间加注解 @Valid,然后在后面加 BindindResult 即可;多个参数,可以添加多个 @Valid 和 BindindResult。
public void test()(@RequestBody @Valid DemoModel demo, BindingResult result)
public void test()(@RequestBody @Valid DemoModel demo, BindingResult result,@RequestBody @Valid DemoModel demo2, BindingResult result2)
@RequestMapping("/demo2")
public void demo2(@RequestBody @Valid DemoModel demo, BindingResult result){
if(result.hasErrors()){
for (ObjectError error : result.getAllErrors()) {
System.out.println(error.getDefaultMessage());
}
}
}
2. GET参数校验(@RequestParam参数校验)
controller @RequestMapping(value = "/demo3", method = RequestMethod.GET)
public void demo3(@RequestParam(name = "grade", required = true) int grade,@RequestParam(name = "classroom", required = true) int classroom) {
System.out.println(grade + "," + classroom);
}
使用 @Valid 注解对 RequestParam 对应的参数时无效的,需要使用 @Validated 注解来使验证生效。
MethodValidationPostProcessor 的 Bean @Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
/**默认是普通模式,会返回所有的验证不通过信息集合*/
return new MethodValidationPostProcessor();
}
或者可对 MethodValidationPostProcessor 进行设置 Validator
此时不是使用 Validator 进行验证,Validator 的配置不起作用 @Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
/**设置validator模式为快速失败返回*/
postProcessor.setValidator(validator());
return postProcessor;
}
@Bean
public Validator validator(){
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
.configure()
.addProperty( "hibernate.validator.fail_fast", "true" )
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
return validator;
}
方法所在的Controller上加注解 @Validated @RequestMapping("/validation")
@RestController
@Validated
public class ValidationController {
/**如果只有少数对象,直接把参数写到Controller层,然后在Controller层进行验证就可以了。*/
@RequestMapping(value = "/demo3", method = RequestMethod.GET)
public void demo3(@Range(min = 1, max = 9, message = "年级只能从1-9")
@RequestParam(name = "grade", required = true)
int grade,
@Min(value = 1, message = "班级最小只能1")
@Max(value = 99, message = "班级最大只能99")
@RequestParam(name = "classroom", required = true)
int classroom) {
System.out.println(grade + "," + classroom);
}
}
返回验证信息提示
验证不通过,抛出来 ConstraintViolationException 异常,使用统一捕获异常处理 @ControllerAdvice
@Component
public class GlobalExceptionHandler {
@ExceptionHandler
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String handle(ValidationException exception) {
if(exception instanceof ConstraintViolationException){
ConstraintViolationException exs = (ConstraintViolationException) exception;
Set> violations = exs.getConstraintViolations();
for (ConstraintViolation> item : violations) {
/**打印验证不通过的信息*/
System.out.println(item.getMessage());
}
}
return "bad request, " ;
}
}
验证 http://localhost:8080/validation/demo3?grade=18&classroom=888
3. model校验
model @Data
public class Demo2 {
@Length(min = 5, max = 17, message = "length长度在[5,17]之间")
private String length;
/**@Size不能验证Integer,适用于String, Collection, Map and arrays*/
@Size(min = 1, max = 3, message = "size在[1,3]之间")
private String age;
@Range(min = 150, max = 250, message = "range在[150,250]之间")
private int high;
@Size(min = 3,max = 5,message = "list的Size在[3,5]")
private List list;
}
校验 @Autowired
private Validator validator;
@RequestMapping("/demo3")
public void demo3(){
Demo2 demo2 = new Demo2();
demo2.setAge("111");
demo2.setHigh(150);
demo2.setLength("ABCDE");
demo2.setList(new ArrayList(){{add("111");add("222");add("333");}});
Set> violationSet = validator.validate(demo2);
for (ConstraintViolation model : violationSet) {
System.out.println(model.getMessage());
}
}
4. 对象级联校验
对象内部包含另一个对象作为属性,属性上加 @Valid,可以验证作为属性的对象内部的验证
demo @Data
public class Demo2 {
@Size(min = 3,max = 5,message = "list的Size在[3,5]")
private List list;
@NotNull
@Valid
private Demo3 demo3;
}
@Data
public class Demo3 {
@Length(min = 5, max = 17, message = "length长度在[5,17]之间")
private String extField;
}
校验 /**前面配置了快速失败返回的Bean*/
@Autowired
private Validator validator;
@RequestMapping("/demo3")
public void demo3(){
Demo2 demo2 = new Demo2();
demo2.setList(new ArrayList(){{add("111");add("222");add("333");}});
Demo3 demo3 = new Demo3();
demo3.setExtField("22");
demo2.setDemo3(demo3);
Set> violationSet = validator.validate(demo2);
for (ConstraintViolation model : violationSet) {
System.out.println(model.getMessage());
}
}
5. 分组校验
校验接口 public interface GroupA {
}
public interface GroupB {
}
demo @Data
public class Person {
@NotBlank
@Range(min = 1,max = Integer.MAX_VALUE,message = "必须大于0",groups = {GroupA.class})
/**用户id*/
private Integer userId;
@NotBlank
@Length(min = 4,max = 20,message = "必须在[4,20]",groups = {GroupB.class})
/**用户名*/
private String userName;
@NotBlank
@Range(min = 0,max = 100,message = "年龄必须在[0,100]",groups={Default.class})
/**年龄*/
private Integer age;
@Range(min = 0,max = 2,message = "性别必须在[0,2]",groups = {GroupB.class})
/**性别 0:未知;1:男;2:女*/
private Integer sex;
}
GroupA校验字段userId
GroupB校验字段userName、sex
Default校验字段age(Default使Validator自带的默认分组)
验证
只验证GroupA和GroupB的分组,以下示例代码 @RequestMapping("/demo5")
public void demo5(){
Person p = new Person();
/**GroupA验证不通过*/
p.setUserId(-12);
/**GroupA验证通过*/
//p.setUserId(12);
p.setUserName("a");
p.setAge(110);
p.setSex(5);
Set> validate = validator.validate(p, GroupA.class, GroupB.class);
for (ConstraintViolation item : validate) {
System.out.println(item);
}
} @RequestMapping("/demo6")
public void demo6(@Validated({GroupA.class, GroupB.class}) Person p, BindingResult result){
if(result.hasErrors()){
List allErrors = result.getAllErrors();
for (ObjectError error : allErrors) {
System.out.println(error);
}
}
}
组序列
指定组的验证顺序,前面组验证不通过,后面组不验证 // GroupA > GroupB > Default
@GroupSequence({GroupA.class, GroupB.class, Default.class})
public interface GroupOrder {
}
5. 自定义校验器
1. 大小写校验器
public enum CaseMode {
UPPER,
LOWER;
}
@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CheckCaseValidator.class)
@Documented
public @interface CheckCase {
String message() default "";
Class>[] groups() default {};
Class extends Payload>[] payload() default {};
CaseMode value();
}
public class CheckCaseValidator implements ConstraintValidator {
private CaseMode caseMode;
public void initialize(CheckCase checkCase) {
this.caseMode = checkCase.value();
}
public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
if (s == null) {
return true;
}
if (caseMode == CaseMode.UPPER) {
return s.equals(s.toUpperCase());
} else {
return s.equals(s.toLowerCase());
}
}
}
2. Demo
public class Demo{
@CheckCase(value = CaseMode.LOWER,message = "userName必须是小写")
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
3. Validator配置
@Bean
public Validator validator(){
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
.configure()
.addProperty( "hibernate.validator.fail_fast", "true" )
.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
return validator;
}
4. 校验测试
@RequestMapping("/demo4")
public void demo4(){
Demo demo = new Demo();
demo.setUserName("userName");
Set> validate = validator.validate(demo);
for (ConstraintViolation dem : validate) {
System.out.println(dem.getMessage());
}
}
6. 常见的注解
No.
注解
解释
01
@Null
检查该字段为空
02
@NotNull
不能为 null
03
@NotBlank
不能为空,检查时会将空格忽略
04
@NotEmpty
不能为空,这里的空是指空字符串
05
@AssertTrue
用于boolwan字段,只能为true
06
@AssertFalse
用于boolwan字段,只能为false
07
@CreditCardNumber
对信用卡进行一个大致的校验
08
@DecimalMin(value)
数值类型,只能小于或等于value
09
@DecimalMax(value)
数值类型,只能大于或等于value
10
@Digits(integer=2,fraction=20)
限制必须为一个小数,整数部分位数不能超过integer,小数部分位数不能超过fraction
11
检查是否是一个有效的email地址
12
@Past
检查该字段的日期是否属于过去的日期
13
@Future
检查该字段的日期是否属于将来的日期
14
@Length(min=,max=)
检查该字段的长度是否在min和max之间,只能用于字符串
15
@Size(min=,max=)
检查该字段的size是否在min和max之间,可以是字符串、数组、集合、map等
16
@Min(value)
小于等于value
17
@Max(value)
大于等于value
18
@URL(protocol=,host,port) 检查是否是一个有效的URL,如果提供来protocol,host等,则该url还需满足提供的条件
19
@Valid
该注解只要用于字段为一个包含其他对象的集合或map或数组的字段,或该字段直接为一个其他对象的引用(这样在检查当前对象的同时也会检查该字段所引用的对象)
7. 参考文档
转载至链接:https://my.oschina.net/shadowolf/blog/1934934