Hibernate Validator官方文档
JSR 303 - Bean Validation - 为实体验证定义了元数据模型和API. 默认的元数据模型是通过 Annotations来描述的,但是也可以使用XML来重载或者扩展. Bean Validation API 并不局限于应 用程序的某一层或者哪种编程模型, 例如,如图所示, Bean Validation 可以被用在任何一层, 或 者是像类似Swing的富客户端程序中.
Hibernate Validator是JSR 303的一种参考实现。
Spring Validation验证框架对参数的验证机制提供了@Validated(Spring's JSR-303规范,是标准JSR-303的一个变种)
似乎可以混用 '_'
-
@Valid属于javax.validation包下,是jdk给提供的
-
@Validated是org.springframework.validation.annotation包下的,是spring提供的
-
@Validated要比@Valid更加强大 支持分组校验
常用的校验注解 @NotNull、@NotBlank....
默认是校验所有字段并返回所有错误信息,也可以配置为校验到一个字段错误就返回结果。
1.添加引用
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.9.Final</version>
<scope>compile</scope>
</dependency>
2.创建实体
package com.zhujun.model;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@Data
public class Student {
public interface GymStudent{};//分组定义接口
//姓名
@NotBlank(message = "姓名未知")
private String name;
//年龄
@NotNull(message = "年龄未知")
private Integer age;
//分组校验
@NotBlank(message = "体育生编号不能为空!",groups = GymStudent.class)
private String gymStudentNo;
}
GymStudent组只会校验gymStudentNo,如果需要校验其他字段,可以使GymStudent extends Default
或者在使用 @Validated({Student.GymStudent.class, Default.class} 声明默认组
Clazz类 持有List
package com.zhujun.model;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.List;
@Data
public class Clazz {
@NotBlank(message = "班级编号不能为空")
private String clazzNo;
@NotNull(message = "班级不能没有学生")
@Valid //嵌套校验 需要使用该注解
private List<Student> studentList;
}
3.检验
package com.zhujun.controller;
import com.zhujun.model.Clazz;
import com.zhujun.model.Student;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import javax.validation.groups.Default;
@Slf4j
@RestController
@RequestMapping("/school")
public class SchoolController {
@RequestMapping("/createStudent")
public String createStudent(@Validated Student student){
//不指定校验分组class 默认为Default.class
log.info(student.toString());
return "success";
}
@RequestMapping("/createClazz")
public String createClazz(@Valid @RequestBody Clazz clazz){
//嵌套校验
log.info(clazz.toString());
return "success";
}
@RequestMapping("/createGymStudent")
public String createGymStudent( @RequestBody @Validated({Student.GymStudent.class, Default.class}) Student student){
//分组校验
log.info(student.toString());
return "success";
}
}
分组校验时可以根据需要 定义组之间校验的顺序
4.统一异常处理
package com.zhujun.config;
import com.zhujun.common.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.List;
@Slf4j
@RestControllerAdvice
public class BadRequestExceptionHandler {
/**
* 校验错误拦截处理
*
* @param exception 错误信息集合
* @return 错误信息
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result validationBodyException(MethodArgumentNotValidException exception) {
BindingResult result = exception.getBindingResult();
if (result.hasErrors()) {
List<ObjectError> errors = result.getAllErrors();
for(ObjectError error:errors){
FieldError fieldError = (FieldError) error;
log.error("Data check failure : object{"+fieldError.getObjectName()+"},field{"+fieldError.getField()+
"},errorMessage{"+fieldError.getDefaultMessage()+"}");
return Result.error(fieldError.getDefaultMessage());//异常信息中 返回一个错误信息即停止
}
}
return null;
}
}
使用@RequestBody参数注解的情况下,抛出的都是MethodArgumentNotValidException异常
不使用时 抛出的则是BindException
啊...这还以为是分组校验导致的,统一异常处理失效了^^
5.手动检验
有时可能会出现Service之间互相调用,没有SpringMVC中的Bind过程,可以在代码中手动进行校验。
public static <T> Map<String, StringBuffer> validate(T obj,Class var) {
Map<String,StringBuffer> errorMap = null;
Set<ConstraintViolation<T>> set = validator.validate(obj, var==null ? Default.class : var);
if(!CollectionUtils.isEmpty(set)){
errorMap = new HashMap<>();
String property = null;
for(ConstraintViolation<T> cv : set){
//这里循环获取错误信息,可以自定义格式
property = cv.getPropertyPath().toString();
if(errorMap.get(property) != null){
errorMap.get(property).append("," + cv.getMessage());
}else{
StringBuffer sb = new StringBuffer();
sb.append(cv.getMessage());
errorMap.put(property, sb);
// 有错误就返回,根据需要是否停止循环
break;
}
}
}
return errorMap;
}
6.默认返回的Json数据格式
{
"timestamp": "2020-05-21T10:56:53.371+0000",
"status": 400,
"error": "Bad Request",
"errors": [
{
"codes": [
"NotNull.student.age",
"NotNull.age",
"NotNull.java.lang.Integer",
"NotNull"
],
"arguments": [
{
"codes": [
"student.age",
"age"
],
"arguments": null,
"defaultMessage": "age",
"code": "age"
}
],
"defaultMessage": "年龄未知",
"objectName": "student",
"field": "age",
"rejectedValue": null,
"bindingFailure": false,
"code": "NotNull"
},
{
"codes": [
"NotBlank.student.name",
"NotBlank.name",
"NotBlank.java.lang.String",
"NotBlank"
],
"arguments": [
{
"codes": [
"student.name",
"name"
],
"arguments": null,
"defaultMessage": "name",
"code": "name"
}
],
"defaultMessage": "姓名未知",
"objectName": "student",
"field": "name",
"rejectedValue": null,
"bindingFailure": false,
"code": "NotBlank"
}
],
"message": "Validation failed for object='student'. Error count: 2",
"path": "/school/createStudent"
}