系列文章目录

文章目录

  • 系列文章目录
  • 一、@Valid和@Validated的介绍
    • 1.引入jar包
    • 2. @Valid和@Validated的作用
    • 3.@Valid和@Validated的区别
    • 4.常用的参数校验注解
  • 二、@Valid和@Validated的使用
    • 1. 捕获全局异常配置类
    • 2.单个接口参数校验
    • 3.接口类参数校验提示
    • 4.嵌套类注解提示
      • 4.1. 实体类数据
      • 4.2.接口校验
      • 4.3. 接口调用
    • 5. 分组校验
      • 5.1 校验的实体类数据
      • 5.2 接口校验
      • 5.3 接口调用
        • 1.提交学习刷题试卷接口演示
        • 2.提交文档学习接口演示
      • 6.分组校验和嵌套校验不能一起使用

一、@Valid和@Validated的介绍

1.引入jar包

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId>
</dependency>

2. @Valid和@Validated的作用

@Validated、@Valid 等校验注解来替代手动对参数进行校验,简单来说就是将参数校验和业务逻辑代码分开。

3.@Valid和@Validated的区别

  1. @Valid:没有分组的功能。
  2. @Valid:可以用在方法、构造函数、方法参数和成员属性(字段)上
  3. @Validated:提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制
  4. @Validated:可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上

4.常用的参数校验注解

二、@Valid和@Validated的使用

1. 捕获全局异常配置类

因为使用注解检验参数会抛出异常,而这个异常是在方法中捕获不到的所以需要全局捕获异常,再根据异常类型提示相应的提示。

package com.fwy.corebasic.common.handlerexception;import com.fwy.common.help.DoResult;
import com.fwy.common.help.DoResultType;
import com.fwy.common.utils.CommonResponse;
import com.fwy.common.utils.LogUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;/*** @author 前端接口和后台参数校验*/
@ControllerAdvice
public class MyExceptionHandler {public final static String[] INTERFACE_PATH_ARRAY = {"api","wxApi"};@ResponseBody@ExceptionHandler(value = {BindException.class,MethodArgumentNotValidException.class})public Object validated(Exception e,HttpServletRequest request) {LogUtil.warn("参数校验异常:{}",request.getRequestURL(), e);List<FieldError> fieldErrors = null;if (e instanceof BindException){BindException e1=(BindException) e;fieldErrors= e1.getBindingResult().getFieldErrors();}else {MethodArgumentNotValidException e2= (MethodArgumentNotValidException) e;fieldErrors =e2.getBindingResult().getFieldErrors();}List<String> validationResults = new ArrayList<>();for (FieldError fieldError : fieldErrors) {validationResults.add(fieldError.getDefaultMessage());}String url = request.getRequestURI();String messages = StringUtils.join(validationResults.toArray(), ";");AtomicBoolean flag = new AtomicBoolean(false);Arrays.stream(INTERFACE_PATH_ARRAY).forEach(path -> {if (url.contains(path)) {flag.set(true);}});if(flag.get()){DoResult result = new DoResult();result.setStateMsg(messages);result.setStateType(DoResultType.fail);return result;}else{CommonResponse result = new CommonResponse();result.setMsg(messages);result.setCode(DoResultType.fail.getValue());return result;}}@ResponseBody@ExceptionHandler(ConstraintViolationException.class)public Object paramException(ConstraintViolationException  e,HttpServletRequest request){LogUtil.warn("参数校验异常:{}",e);String url = request.getRequestURI();AtomicBoolean flag = new AtomicBoolean(false);Arrays.stream(INTERFACE_PATH_ARRAY).forEach(path -> {if (url.contains(path)) {flag.set(true);}});if(flag.get()){DoResult result = new DoResult();List<String> msgList = new ArrayList<>();for (ConstraintViolation<?> constraintViolation : e.getConstraintViolations()) {msgList.add(constraintViolation.getMessage());}String messages = StringUtils.join(msgList.toArray(), ";");result.setStateMsg(messages);result.setStateType(DoResultType.fail);return result;}else{CommonResponse result = new CommonResponse();List<String> msgList = new ArrayList<>();for (ConstraintViolation<?> constraintViolation : e.getConstraintViolations()) {msgList.add(constraintViolation.getMessage());}String messages = StringUtils.join(msgList.toArray(), ";");result.setMsg(messages);result.setCode(DoResultType.fail.getValue());return result;}}/*** 违反约束异常处理* @param e* @return*/@ExceptionHandler(ConstraintViolationException.class)@ResponseBodypublic Object  handConstraintViolationException(ConstraintViolationException e) {LogUtil.error(e.getMessage(), e);return e;}@ExceptionHandler(Exception.class)public Object exceptionHand(Exception e, HttpServletRequest request, HttpServletResponse response) {LogUtil.error("发生异常的请求地址{}",request.getRequestURL(),e);//如果是ajax则返回一个CommonResponse ,否则跳转errorif (request.getHeader("X-Requested-With") != null &&"XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {String url = request.getRequestURI();AtomicBoolean flag = new AtomicBoolean(false);Arrays.stream(INTERFACE_PATH_ARRAY).forEach(path -> {if (url.contains(path)) {flag.set(true);}});if(flag.get()){DoResult result = new DoResult();result.setStateMsg("系统异常");result.setStateType(DoResultType.fail);return result;}else{CommonResponse result = new CommonResponse();result.setMsg("系统异常");result.setCode(DoResultType.fail.getValue());return result;}} else {ModelAndView mav = new ModelAndView();mav.addObject("msg", e.getMessage());mav.addObject("code", response.getStatus());mav.addObject("uri", request.getRequestURI());mav.setViewName("error");return mav;}}
}

2.单个接口参数校验

   /***** description: 获取学习试题数据接口* version: 1.0 ->* date: 2022/5/17 11:45* author: xiaYZ* iteration: 迭代说明* @param deptId    部门id* @param weiXinUserId   微信用户id* @param businessId 业务id* @return com.fwy.common.help.DoResult*/@GetMapping("getStudyQuestionPaper")@Validatedpublic DoResult getStudyQuestionPaper(@NotNull(message = "部门id不能为空") Long deptId,@NotNull(message = "微信用户id不能空") Long weiXinUserId,@NotNull(message = "业务id不能为空") Long businessId){DoResult result = new DoResult();return result;}

如图展示,当业务id为空是提示参数校验信息

3.接口类参数校验提示

    /**** description: saveStageQuestion* version: 1.0* date: 2020/4/24 17:58* author: objcat* @param questionBank* @return com.jrwp.common.help.CommonResponse*/@ResponseBody@RequestMapping("saveQuestion")@Description("保存题库数据")public CommonResponse saveQuestion(@Validated QuestionBank questionBank){CommonResponse response = new CommonResponse();return response;}
@TableName(value ="QUESTION_BANK")
@KeySequence(value = "SEQ_QUESTION_BANK")
@Data
public class QuestionBank implements Serializable {public static final String REDIS_KEY = "QUESTION_BANK";/*** 主键*/@TableIdprivate Long id;/*** 知识体系id(数据字典中)*/@TableField(value = "KNOWLEDGE_ID")private Long knowledgeId;/*** 目类型(0单选题,1多选题)*/@TableField(value = "QUESTION_TYPE")private Integer questionType;/*** 是否启用(0否,1是)*/@TableField(value = "IS_START")private Integer isStart;/*** 题目标题*/@TableField(value = "TITLE")@Length(max = 500,message = "题目标题长度不能超过500")private String title;/*** 题目答案*/@TableField(value = "ANSWER")@Length(max = 20,message = "题目答案长度不能超过20")private String answer;/*** 说明*/@TableField(value = "REMARKS")@Length(max = 500,message = "说明长度不能超过500")private String remarks;/*** 题目选项用|分割*/@TableField(value = "OPTIONS")@Length(max = 500,message = "题目选项长度不能超过500")private String options;/*** 创建时间*/@TableField(value = "CREATE_TIME")@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8",locale = "zh")private Date createTime;/*** 知识体系名称*/@TableField(value = "KNOWLEDGE_NAME")private String knowledgeName;/*** 题目图片路径*/@TableField(value = "PhOTO_URL")private String photoUrl;@TableField(exist = false)private static final long serialVersionUID = 1L;
}

如图提示:当数据字段长度超过注解中注释的则会提示信息

4.嵌套类注解提示

当我们需要校验嵌套类时,如A包含B,C两个类(A:{B,C}),需要检验B中b字段。

4.1. 实体类数据

@Data
public class SubmitStudyParam {/*** 学习记录*/@Valid@NotNull(message = "学习记录不能为空")private StudyRecordApi studyRecordApi;/*** 业务名称*/@NotEmpty(message = "业务名称不能为空")private String businessName;/*** 部门id*/@NotNull(message = "部门id不能为空")private Long deptId;/*** 部门名称*/@NotEmpty(message = "部门名称不能为空")private String deptName;/*** 当前刷题id*/private Long nowQuestionId;
}
@Data
public class StudyRecordApi implements Serializable {/*** 主键*/private Long id;/*** 注册用户id*/@NotNull(message = "注册用户id不能为空")private Long registerUserId;/*** 注册用户姓名*/@NotEmpty(message = "注册用户姓名不能为空")private String registerUserName;/*** 学习类型(0刷题学习,1文档学习,2视频学习)*/@NotNull(message = "学习类型不能为空")private Integer type;/*** 事件id,不同类型对应不同表,0时对应刷题学习表,1,2时对应文档表*/private Long eventId;/*** 学习开始时间*/private Date startTime;/*** 开始学习时间字符串*/@NotEmpty(message = "开始学习时间不能为空")private String startTimeStr;/*** 学习结束时间*/private Date endTime;/*** 学习结束时间字符串*/@NotEmpty(message = "学习结束时间不能为空")private String endTimeStr;/*** 创建时间*/private Date createTime;/*** 申请记录id*/@NotNull(message = "申请记录id不能为空")private Long applyId;private static final long serialVersionUID = 1L;
}

4.2.接口校验

   /***** description: 提交文档学习* version: 1.0 ->* date: 2022/5/18 11:50* author: xiaYZ* iteration: 迭代说明* @param submitStudyParam* @return com.fwy.common.help.DoResult*/@ResponseBody@PostMapping("submitStudyFile")public DoResult submitStudyFile(@Validated @RequestBody SubmitStudyParam submitStudyParam){DoResult result = new DoResult();boolean flag = studyService.submitStudyFile(submitStudyParam);if(flag){result.setStateMsg("提交文档学习成功");result.setStateType(DoResultType.success);}else{result.setStateMsg("提交文档学习失败");result.setStateType(DoResultType.fail);}return result;}

4.3. 接口调用

5. 分组校验

5.1 校验的实体类数据

@Data
public class SubmitStudyParam {/*** 练习题目*/@NotNull(message = "练习题目不能为空",groups = {StudyService.class})@Validprivate PracticePaper practicePaper;/*** 学习记录*/@Valid@NotNull(message = "学习记录不能为空")private StudyRecordApi studyRecordApi;/*** 业务名称*/@NotEmpty(message = "业务名称不能为空")private String businessName;/*** 部门id*/@NotNull(message = "部门id不能为空")private Long deptId;/*** 部门名称*/@NotEmpty(message = "部门名称不能为空")private String deptName;/*** 当前刷题id*/private Long nowQuestionId;
}

注意:PracticePaper类注解中有一个group参数

5.2 接口校验

  /***** description: 提交文档学习* version: 1.0 ->* date: 2022/5/18 11:50* author: xiaYZ* iteration: 迭代说明* @param submitStudyParam* @return com.fwy.common.help.DoResult*/@ResponseBody@PostMapping("submitStudyFile")public DoResult submitStudyFile(@Validated @RequestBody SubmitStudyParam submitStudyParam){DoResult result = new DoResult();boolean flag = studyService.submitStudyFile(submitStudyParam);if(flag){result.setStateMsg("提交文档学习成功");result.setStateType(DoResultType.success);}else{result.setStateMsg("提交文档学习失败");result.setStateType(DoResultType.fail);}return result;}
 /***** description: 提交学习刷题试卷接口* version: 1.0 ->* date: 2022/5/18 10:26* author: xiaYZ* iteration: 迭代说明* @param submitStudyParam 提交学习刷题试卷参数* @return com.fwy.common.help.DoResult*/@ResponseBody@PostMapping("submitStudyPaper")public DoResult submitStudyPaper(@Validated({StudyService.class}) @RequestBody SubmitStudyParam submitStudyParam){DoResult result = new DoResult();boolean flag = studyService.submitStudyPaper(submitStudyParam);if(flag){result.setStateMsg("提交学习试卷成功");result.setStateType(DoResultType.success);}else{result.setStateMsg("提交学习试卷失败");result.setStateType(DoResultType.fail);}return result;}

两个接口都使用了SubmitStudyParam 作为接口参数,但是“提交学习刷题试卷接口”,我需要它校验“练习题目类”是否为空,但是“提交文档学习接口”则不需要,所以我使用了@Validated中的group参数

5.3 接口调用

1.提交学习刷题试卷接口演示


2.提交文档学习接口演示


6.分组校验和嵌套校验不能一起使用

换句话说当你使用@Valid做嵌套检验是,再使用group参数做分组校验时,它不能校验@Valid注解类中的字段

刷题学习类的校验提示

@Data
public class PracticePaper implements Serializable {/*** 主键*/private Long id;/*** 注册用户id*/@NotNull(message = "注册用户id不能为空")private Long registerUserId;/*** 题目id列表*/@NotEmpty(message = "题目id列表不能为空")private String questionIdList;/*** 提交答案列表*/@NotEmpty(message = "提交答案列表不能为空")private String submitAnswerList;/*** 实际答案列表*/@NotEmpty(message = "实际答案列表不能为空")private String answerList;/*** 创建时间*/private Date createTime;private static final long serialVersionUID = 1L;
}

解释:如图我这里的开始学习时间字段为空,但是提交接口后没有为空的提示,直接报错了

springBoot学习笔记(6)——@Valid和@Validated的使用相关推荐

  1. SpringBoot 学习笔记

    SpringBoot 学习笔记 文章目录 SpringBoot 学习笔记 1. SpringBoot简介 1.1 什么是Spring 1.2 Spring 是如何简化Java开发的 1.3 什么是 S ...

  2. SpringBoot(学习笔记)

    SpringBoot学习笔记 从今天开始就进入微服务阶段 一些小问题 1.HelloWorld 1.1回顾什么是Spring 1.2什么是SpringBoot 1.3微服务架构 2.第一个Spring ...

  3. springboot学习笔记:12.解决springboot打成可执行jar在linux上启动慢的问题

    springboot学习笔记:12.解决springboot打成可执行jar在linux上启动慢的问题 参考文章: (1)springboot学习笔记:12.解决springboot打成可执行jar在 ...

  4. SpringBoot学习笔记(3):静态资源处理

    SpringBoot学习笔记(3):静态资源处理 在web开发中,静态资源的访问是必不可少的,如:Html.图片.js.css 等资源的访问. Spring Boot 对静态资源访问提供了很好的支持, ...

  5. springboot学习笔记(五)

    一丶注值方式 1.在application.properties文件中注值 首先我们将application.yml中的学生名字和年龄给注释掉,来验证在applic.properties的注值方式. ...

  6. SpringBoot学习笔记(4)----SpringBoot中freemarker、thymeleaf的使用

    1. freemarker引擎的使用 如果你使用的是idea或者eclipse中安装了sts插件,那么在新建项目时就可以直接指定试图模板 如图: 勾选freeMarker,此时springboot项目 ...

  7. SpringBoot学习笔记(16):单元测试

    SpringBoot学习笔记(16):单元测试 单元测试 单元测试(英语:Unit Testing)又称为模块测试,是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作.程序单元是应用的最小 ...

  8. SpringBoot学习笔记(9)----SpringBoot中使用关系型数据库以及事务处理

    在实际的运用开发中,跟数据库之间的交互是必不可少的,SpringBoot也提供了两种跟数据库交互的方式. 1. 使用JdbcTemplate 在SpringBoot中提供了JdbcTemplate模板 ...

  9. SpringBoot学习笔记(16)----SpringBoot整合Swagger2

    Swagger 是一个规范和完整的框架,用于生成,描述,调用和可视化RESTful风格的web服务 http://swagger.io Springfox的前身是swagger-springmvc,是 ...

  10. SpringBoot学习笔记(8):事物处理

    SpringBoot学习笔记(8):事物处理 快速入门 在传统的JDBC事务代码开发过程中,业务代码只有一部分,大部分都是与JDBC有关的功能代码,比如数据库的获取与关闭以及事务的提交与回滚.大量的t ...

最新文章

  1. 一种在注入进程中使用WTL创建无焦点不在任务栏出现“吸附”窗口的方法和思路
  2. Lightoj1014【基础题】
  3. IntelliJ IDEA的光芒会盖过Eclipse吗
  4. 浅谈二次元场景特征和绘制手法
  5. imagettftext php7,mac php7 imagettftext
  6. python程序中想使用正则表达式_python中正则表达式的使用方法
  7. 新网卡不能绑定“旧”IP故障的解决
  8. 2018.10.27 bzoj3209: 花神的数论题(数位dp)
  9. 戴尔企业级技术社区达人积分等级制度
  10. Windows搭建Sosoapi
  11. 方志仅占古籍数量十分之一,在家谱编修中却举足轻重,方志凭什么
  12. PBS常用基本命令------PBS入门
  13. 在python中month函数的用法_python @classmethod 的使用场合
  14. strtus1 html5,struts1.x的入门级学习教程
  15. 码农的自我修养 - ARM V9架构的巨大价值
  16. python 贪吃蛇大作战_Python实现贪吃蛇大作战
  17. 根据经纬度信息画实际地图中的轨迹之百度地图与谷歌地球
  18. 电信android智能机定位问题
  19. 管理会计报告和财务报告的区别
  20. 【Linux入门学习日记】ubuntu启动卡在logo界面

热门文章

  1. SkipList(跳跃表)详解
  2. unity shader 溶解,上下左右,cutoff
  3. python函数查询工具_布同:Python函数帮助查询小工具[v1和v2]
  4. Jquery UI常用插件
  5. 聚搜-聚合搜索引擎网页模板
  6. 一句话,连上隔壁老王家的 WiFi !
  7. 已分区的硬盘如何重新合并, 分出去的盘怎么重新合并
  8. c语言实现7段数码管显示,FPGA入门--七段数码管显示
  9. 利用 Python 爬取58同城品牌公寓(附带租房指南)
  10. html编辑器怎么设置为excel,excel2013宏编辑器的设置方法教程