Hibernate Validator使用

Hibernate Validator官方文档

https://docs.jboss.org/hibernate/validator/4.2/reference/zh-CN/html_single/#validator-usingvalidator-validationgroups

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"
}
原文地址:https://www.cnblogs.com/shinyrou/p/12932738.html