IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> springBoot学习笔记(6)——@Valid和@Validated的使用 -> 正文阅读

[Java知识库]springBoot学习笔记(6)——@Valid和@Validated的使用

作者:token annotation punctuation

系列文章目录


一、@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)
    @ResponseBody
    public 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 ,否则跳转error
        if (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")
    @Validated
    public 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";
    /**
     * 主键
     */
    @TableId
    private 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})
    @Valid
    private 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;
}

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

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-05-21 18:48:02  更:2022-05-21 18:50:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 12:14:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码