Spring Boot 实战:基于 Validation 注解实现分层数据校验与校验异常拦截器统一返回处理

1. 概述

本文介绍了在spring boot框架下,使用validation数据校验注解,针对不同请求链接的前端传参数据,进行分层视图对象的校验,并通过配置全局异常处理器捕获传参校验失败异常,自动返回校验出错的异常数据。

2. 依赖包导入

导入校验注解,本文使用的是spring boot框架下的validation校验包,首先将如下代码配置到pom.xml中并更新maven

XML 复制代码
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

3. 校验规则注解

在java Bean中针对需要校验的属性字段编写对应的校验注解

3.1 视图对象分层的意义

对同一个数据库中同一张表进行不同操作,前端传入的入参校验法则是不一样的,甚至相互之间还有冲突,因此针对同一个javaBean使用同一套入参数据校验是不可行的。

举个例子,比如对如下java Bean数据进行新增操作和更新操作,其ID字段的校验规则就完全冲突,新增时,要求前端不传入,因为这个字段是数据库自增字段。而在更新操作时,ID字段是必传字段,因为是通过这个字段定位到具体要修改的那条数据。因此需要根据不同的入参需求建立分层视图对象,进行分别校验。

java 复制代码
@Data
public class Employee {
    private Long id;
    private String name;
    private Integer age;
    private  String email;
    private String gender;
    private String address;
    private BigDecimal salary;
}

3.2 分层对象的设计模式

设计模式:单一职责 (新建和修改用到的校验规则不一致)

JavaBean也要分层,各种xxO:

Pojo:普通java类

Dao:Database Access Object : 专门用来访问数据库的对象

DTO:Data Transfer Object: 专门用来传输数据的对象;

TO:transfer Object: 专门用来传输数据的对象;

BO:Business Object: 业务对象(Service),专门用来封装业务逻辑的对象;

VO:View/Value Object: 值对象,视图对象(专门用来封装前端数据的对象)

3.3 视图对象最佳实践举例

根据如上设计模式,本文分别针对新增和更新操作建立两个视图对象,如下所示:

新增视图对象,仅供新增接口使用的入参接收对象

java 复制代码
@Data
public class EmployeeAddVo {
    @NotBlank(message = "姓名不能为空")
    private String name;

    @NotNull(message = "年龄不能为空")
    @Max(value = 150, message = "年龄不能超过150岁")
    @Min(value = 0, message = "年龄不能小于0岁")
    private Integer age;


    @Email(message = "邮箱格式不正确")
    private String email;

    @Pattern(regexp = "^男|女$",message = "性别只能为男或者女")
    private String gender;
    private String address;
    private BigDecimal salary;
}

更新视图对象,仅供更新接口使用的入参接收对象

java 复制代码
@Data
public class EmployeeUpdateVo {
    @NotNull(message = "id不能为空")
    private Long id;

    private String name;

    @Max(value = 150, message = "年龄不能超过150岁")
    @Min(value = 0, message = "年龄不能小于0岁")
    private Integer age;

    @Email(message = "邮箱格式不正确")
    private String email;

    @Pattern(regexp = "^男|女$",message = "性别只能为男或者女")
    private String gender;

    private String address;

    private BigDecimal salary;
}

3.4 非空校验注解混淆区分

上述注解中运用到了非空校验注解,@NotNull、@NotEmpty、@NotBlank 都是用于在数据校验中检查字段值是否为空的注解,但是它们的用法和校验规则有所不同。

@NotNull (包装类型不为null) : @NotNull 注解是 JSR 303 规范中定义的注解,当被标注的字段值为 null 时,会认为校验失败而抛出异常。该注解不能用于字符串类型的校验,若要对字符串进行校验,应该使用 @NotBlank 或 @NotEmpty 注解。

@NotEmpty (集合类型长度大于0) :@NotEmpty 注解同样是 JSR 303 规范中定义的注解,对于 CharSequence、Collection、Map 或者数组对象类型的属性进行校验,校验时会检查该属性是否为 Null 或者 size()==0,如果是的话就会校验失败。但是对于其他类型的属性,该注解无效。需要注意的是只校验空格前后的字符串,如果该字符串中间只有空格,不会被认为是空字符串,校验不会失败。

@NotBlank (字符串,不为null,切不为" "字符串): @NotBlank 注解是 Hibernate Validator 附加的注解,对于字符串类型的属性进行校验,校验 时会检查该属性是否为 Null 或 "" 或者只包含空格,如果是的话就会校验失败。需要注意的是,@NotBlank 注解只能用于字符串类型的校验。

4. 校验生效注解

在controller层使用 @Valid 告诉 SpringMVC 进行校验,通过在入参中作用@Valid注解,让视图对象中各个属性的数据校验生效,见如下代码:

java 复制代码
@RestController
@CrossOrigin
@RequestMapping("api/v1")
public class EmployeeRestController {
    @Autowired
    private EmployeeService employeeService;
    /**
     * 新增员工;
     * 要求:前端发送请求把员工的json放在请求体中
     * @param employee
     * @return
     */
    @PostMapping("/employee")
    public R add(@RequestBody @Valid EmployeeAddVo vo){
        //把vo转为do;
        Employee employee = new Employee();
        //属性对拷
        BeanUtils.copyProperties(vo,employee);
        employeeService.saveEmp(employee);
        return R.ok();
    }

    /**
     * 修改员工
     * 要求:前端发送请求把员工的json放在请求体中; 必须携带id
     * @param vo
     * @return
     */
    @PutMapping("/employee")
    public R update(@RequestBody @Valid EmployeeUpdateVo vo){
        Employee employee = new Employee();
        BeanUtils.copyProperties(vo,employee);

        employeeService.updateEmp(employee);
        return R.ok();
    }

}

同时还需要配合使用BeanUtils.copyProperties方法,将视图对象对拷贝到数据库对象中,完成数据库的操作。

5. 失败返回处理

编写全局异常处理器,统一返回校验失败提示信息。根据上述@Valid注解作用于入参后,如果数据校验不通过,controller层会自动抛出校验不通过异常:MethodArgumentNotValidException,因此只需要使用全局拦截器捕获这个异常进行统一处理即可。

java 复制代码
@RestControllerAdvice // @ControllerAdvice + @ResponseBody
public class GlobalExceptionHandler {
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R handleMethodArgumentNotValidException(MethodArgumentNotValidException e){
        BindingResult result = e.getBindingResult();
        Map<String,String> errorsMap = new HashMap<>();
        for (FieldError fieldError : result.getFieldErrors()) {
            // 1. 获取到属性名
            String field = fieldError.getField();
            // 2. 获取到错误信息
            String defaultMessage = fieldError.getDefaultMessage();
            errorsMap.put(field,defaultMessage);
        }
        return R.error(500,"校验失败",errorsMap);
    }
}

6. 最终效果展示

通过如上操作后,当进行新增和更新访问请求数据校验不通过时,就会出发数据校验失败异常,通过全局拦截器给前端返回需要的提示的内容:

新增数据校验示例:

更新数据校验示例:

相关推荐
IT-sec5 分钟前
jquery-picture-cut 任意文件上传(CVE-2018-9208)
android·前端·javascript·安全·web安全·网络安全·jquery
HUT_Tyne26513 分钟前
力扣--LCR 154.复杂链表的复制
java·leetcode·链表
黄昏_14 分钟前
在Springboot项目中实现将文件上传至阿里云 OSS
java·spring boot·后端·阿里云
Rverdoser23 分钟前
html渲染优先级
前端·html
期待未来的男孩35 分钟前
安全加固方案
java·网络·安全
2301_8112743135 分钟前
基于Vue+SpringBoot的考研学习分享平台设计与实现
vue.js·spring boot·考研
程序员阿龙36 分钟前
基于SpringBoot的京东绿谷旅游信息服务平台设计与实现(源码+定制+开发)
spring boot·毕业设计·旅游·京东绿谷智慧旅游管理平台·景点推荐与旅游预订系统设计·智能化旅游推荐与信息查询系统·高效旅游资源管理与用户服务平台
惜.己36 分钟前
Jmeter中的配置原件
java·前端·数据库
吾日三省吾码1 小时前
JVM-类文件结构
后端
zhuzhihongNO11 小时前
JVM(JAVA虚拟机)内存溢出导致内存不足,Java运行时环境无法继续
java·开发语言·jvm·内存溢出·jvm内存溢出