一、数据校验
在web开发时,对于请求参数,一般上都需要进行参数合法性校验的,原先的写法时一个个字段一个个去判断,这种方式太不通用了,所以java的JSR 303: Bean Validation 1.0 规范就是解决这个问题的。JSR 303只是个规范,并没有具体的实现,目前通常都是用hibernate-validator进行统一参数校验。
二、Hibernate Validator 版本
Hibernate Validator是Hibernate团队最初的验证框架,现在被称为“Legacy Hibernate Validator”。Hibernate Validator 4.x是Bean Validation 1.0(JSR 303)的参考实现,而Hibernate Validator 5.x是Bean Validation 1.1(JSR 349)的参考实现,目前最新版的Hibernate Validator 6.x 是Bean Validation 2.0(JSR 380)的参考实现。
三、hibernate validator校验demo
spring-boot-starter-web包里面有hibernate-validator包,不需要引用hibernate validator依赖。
3.1 校验对象数据
给bean配置Validation Annotations
import javax.validation.constraints.*; // 注:不是用org.hibernate.validator.constraints
public class DemoModel {
@NotBlank(message="用户名不能为空")
@Length(min = 1, max = 20, message = "用户名应为1~20个字符")
private String userName;
@NotBlank(message="年龄不能为空")
@Pattern(regexp="^[0-9]{1,2}$",message="年龄不正确")
@Min(value = 18, message = "用户年龄必须大于18岁")
@Max(value = 150, message = "用户年龄必须小于150岁")
private String age;
@AssertFalse(message = "必须为false")
private Boolean isFalse;
//如果是空,则不校验,如果不为空,则校验
@Pattern(regexp="^[0-9]{4}-[0-9]{2}-[0-9]{2}$",message="出生日期格式不正确")
private String birthday;
@NotEmpty
@Email(message = "邮箱格式不正确")
private String email;
// get set 省略.....
}
POST接口验证,BindingResult是验证不通过的格式化后的结果集合(如果不写,会返回一堆英文信息)
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@RestController
@RequestMapping(value = "/demo")
public class HibernateValidatorController {
@PostMapping("/2")
public void demo2(@RequestBody @Valid DemoModel demo, BindingResult result){
//POST请求传入的参数:{"userName":"dd","age":160,"isFalse":true,"birthday":"21010-21-12","email":"alanchen@"}
if(result.hasErrors()){
for (ObjectError error : result.getAllErrors()) {
System.out.println(error.getDefaultMessage());
}
}
}
}
验证请求参数时,在@RequestBody DemoModel demo之间加注解 @Valid,然后后面加BindindResult即可;多个参数的,可以加多个@Valid和BindingResult
public void test()(@RequestBody @Valid DemoModel demo, BindingResult result)
public void test()(@RequestBody @Valid DemoModel demo, BindingResult result,@RequestBody @Valid DemoModel demo2, BindingResult result2)
3.2 GET参数校验(@RequestParam参数校验)
使用@Valid注解,对RequestParam对应的参数进行注解,是无效的,需要在方法所在的Controller上加注解@Validated
@Validated
@RestController
@RequestMapping(value = "/demo")
public class HibernateValidatorController {
@GetMapping("/1")
public String demo1(@Email @RequestParam(required = true) String email) {
return "Hello SpringBoot "+email;
}
}
3.3 hibernate的校验模式
Hibernate Validator有以下两种验证模式:
- 1、普通模式(默认是这个模式):普通模式会校验完所有的属性,然后返回所有的验证失败信息
- 2、快速失败返回模式:快速失败返回模式只要有一个验证失败,则返回
配置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;
}
}
3.4返回验证信息提示
验证不通过时,抛出了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, " ;
}
}
3.5 使用的是JSR annotations
所有的注解,应该使用JSR注解,即javax.validation.constraints,而不是org.hibernate.validator.constraints
@NotNull – validates that the annotated property value is not null
@AssertTrue – validates that the annotated property value is true
@Size – validates that the annotated property value has a size between the attributes min and max; can be applied to String, Collection, Map, and array properties
@Min – vValidates that the annotated property has a value no smaller than the value attribute
@Max – validates that the annotated property has a value no larger than the value attribute
@Email – validates that the annotated property is a valid email address
@NotEmpty – validates that the property is not null or empty; can be applied to String, Collection, Map or Array values
@NotBlank – can be applied only to text values and validated that the property is not null or whitespace
@Positive and @PositiveOrZero – apply to numeric values and validate that they are strictly positive, or positive including 0
@Negative and @NegativeOrZero – apply to numeric values and validate that they are strictly negative, or negative including 0
@Past and @PastOrPresent – validate that a date value is in the past or the past including the present; can be applied to date types including those added in Java 8
@Future and @FutureOrPresent – validates that a date value is in the future, or in the future including the present
四、关于前后端双层校验
项目中一般是前后端对数据进行双层验证,为什么不用一种就够了?前后端都写验证,会不会重复且浪费开发资源?简单来说
- 1、前端验证是防“君子”,而后台验证是防“小人”
- 2、不做前端数据验证,只做后端数据验证弊端:一般前端验证都有比较好的开源插件,使用简单且交互友好,比如邮箱验证,当用户输入一个错误的邮箱地址,输入光标离开输入框后,验证框架立即就会给出比较清晰友好的提示:邮箱格式错误,交互性更好。如果不做前端验证,只做后端验证,那么需要用户提交数据到后台后才能得到邮箱格式错误的提示,提示时间滞后,且多了一次无效的网络请求。
- 3、不做后端数据验证,只做前端数据验证弊端:当“黑客”绕过前端,直接访问后端服务时,如果后端不做数据校验,就相当于战士上战场不穿防弹衣。
参考文章
- http://hibernate.org/validator/faq/
- https://www.baeldung.com/javax-validation
- https://www.cnblogs.com/mr-yang-localhost/p/7812038.html