SpringBoot参数校验@Valid 和 @Validated注解使用详解

JSR-303 是 JAVA EE 6 中的一项子规范,叫做 Bean Validation,官方参考实现是Hibernate Validator。

注意:JSR-303实现与 Hibernate ORM 没有任何关系。 JSR 303 用于对 Java Bean 中的字段的值进行验证。

Spring MVC 3.x 之中也大力支持 JSR-303,可以在控制器中对表单提交的数据方便地验证。

在这里插入图片描述

一、@Valid注解使用

1、使用步骤

在Controller中使用 @Valid+BindingResult做参数校验非常好用。一般步骤如下:

(1)引入pom依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
<!-- @JsonFormat注解需要引入-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
        </dependency>

(2)在入参bean类中需要校验的参数上使用校验注解

  • 在bean类中的字段加上校验注解,比如:@NotBlank(message = “ID 不能为空”)
  • 每个字段可以加上多个校验注解。
  • 如果 bean需要交给Spring容器管理,可以加上 @Conmponent注解。

(3)编写Controller层代码

  • 在入参的请求体前加@Valid注解
  • 请求体后加 BindingResult bindingResult 此类为bean校验捕获到的异常储存。
  • 对于 BindingResult信息,我们业务做统一校验信息处理。

注意:

(1)@Valid与 BindingResult要配套使用。

(2)@Valid使用

  • 可以用在方法、构造函数、方法参数和成员属性(field)上;
  • 可以进行嵌套校验,但是,需要在嵌套的字段上面加上@Valid注解;
  • 不能进行分组校验;

2、示例1使用

(1)请求体

@Data
public class AaSaveRequest extends BaseRequest {
    private static final long serialVersionUID = -7895168538160321157L;

    @NotEmpty(message = "用户名不能为空")
    private String username;

    @NotNull(message = "createTime不能为空")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date createTime;

    private Boolean delFlag;
    
}

(2)controller请求方法

    @PostMapping("/save")
    public WebResult save(@RequestBody @Valid AaSaveRequest saveRequest, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            //如果有校验错误,返回第一个校验信息
            String defaultMessage = bindingResult.getFieldErrors().get(0).getDefaultMessage();

            //将错误信息返回或者抛异常全局处理。
            WebResult webResult = new WebResult();
            webResult.setSuccess(false);
            webResult.setMessage(defaultMessage);
            return webResult;
        }
        BaseOperateResult operateResult = aaService.save(saveRequest);
        if (!operateResult.isSuccess()) {
            WebResult.error().message(operateResult.getErrorCode().getMessage());
        }
        return WebResult.ok();
    }

bindingResult.hasErrors() 判断是否校验通过,未通过bindingResult.getFieldError().getDefaultMessage()获取在TestEntity的属性设置的自定义message,如果没有设置,则返回默认值"javax.validation.constraints.XXX.message"。

(3)测试访问

在这里插入图片描述

3、示例2使用

我们将实示例1中 controller请求方法对于 校验错误采用全局异常捕获的方式处理。

(1)自定义异常类

自定义异常,或者捕获 MethodArgumentNotValidException异常。

/**
 * 参数校验异常类
 */
public class ParameterCheckException extends RuntimeException {
    private static final long serialVersionUID = -4163815498400849756L;

    public ParameterCheckException(String message) {
        super(message);
    }

}

(2)全局异常处理

/**
 * 全局异常处理类
 */
@Slf4j
@ResponseBody
@ControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(value = ParameterCheckException.class)
    private WebResult handlerParameterCheckException(ParameterCheckException e) {
        //log.error("ParameterCheckException 异常 -> error = {}", e);
        WebResult webResult = new WebResult();
        webResult.setSuccess(false);
        webResult.setCode(WebHttpCode.SERVICE_ERROR);
        webResult.setMessage(e.getMessage());
        return webResult;
    }

}

(3)controller请求方法改造

    @PostMapping("/save")
    public WebResult save(@RequestBody @Valid AaSaveRequest saveRequest, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            //如果有校验错误,返回第一个校验信息
            String defaultMessage = bindingResult.getFieldErrors().get(0).getDefaultMessage();
            //将错误信息返回或者抛异常全局处理。
            throw new ParameterCheckException(defaultMessage);
        }
        BaseOperateResult operateResult = aaService.save(saveRequest);
        if (!operateResult.isSuccess()) {
            WebResult.error().message(operateResult.getErrorCode().getMessage());
        }
        return WebResult.ok();
    }

测试访问校验ok

4、嵌套使用

嵌套使用,比如:A类中引用B类,且A、B二类都有内部校验,为了使B类也生效,在A类中引用B类时,在B类变量上加@Valid注解,如果B类不能为空时还需要再加@NotNull。

注意:嵌套验证时必须使用 @Valid注解。

(1)请求体

@Data
public class AaSave2Request extends BaseRequest {
    private static final long serialVersionUID = -7895168538160321157L;

    @NotEmpty(message = "用户名不能为空")
    private String username;

    @NotNull(message = "createTime不能为空")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date createTime;

    private Boolean delFlag;

    /**
     * 嵌套验证时必须使用 @Valid注解
     */
    @Valid
    @NotNull(message = "文件不能为空")
    private AaSaveFileRequest aaSaveFileRequest;
    
}

(2)controller请求方法

    @PostMapping("/save2")
    public WebResult save2(@RequestBody @Valid AaSave2Request save2Request, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            //如果有校验错误,返回第一个校验信息
            String defaultMessage = bindingResult.getFieldErrors().get(0).getDefaultMessage();
            //将错误信息返回或者抛异常全局处理。
            throw new ParameterCheckException(defaultMessage);
        }
        BaseOperateResult operateResult = aaService.save2(save2Request);
        if (!operateResult.isSuccess()) {
            WebResult.error().message(operateResult.getErrorCode().getMessage());
        }
        return WebResult.ok();
    }

(3)测试访问

在这里插入图片描述

二、@Validated注解使用

1、示例1使用

(1)请求体

@Data
public class AaUpdateRequest extends BaseRequest {
    private static final long serialVersionUID = -8386003857309634939L;

    @NotNull(message = "id不能为空")
    private Long id;

    @NotEmpty(message = "username不能为空")
    private String username;

    @NotNull(message = "createTime不能为空")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date createTime;

    private Boolean delFlag;

}

(2)controller请求方法

    @PostMapping("/update")
    public WebResult update(@RequestBody @Validated AaUpdateRequest updateRequest, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            //如果有校验错误,返回第一个校验信息
            String defaultMessage = bindingResult.getFieldErrors().get(0).getDefaultMessage();
            throw new ParameterCheckException(defaultMessage);
        }
        BaseOperateResult operateResult = aaService.update(updateRequest);
        if (!operateResult.isSuccess()) {
            WebResult.error().message(operateResult.getErrorCode().getMessage());
        }
        return WebResult.ok();
    }

3)测试访问

在这里插入图片描述

2、示例2使用

我们将实示例1中 controller请求方法对于 校验错误采用全局异常捕获的方式处理。

(1)全局异常处理,捕获 MethodArgumentNotValidException异常

在这里插入图片描述

/**
 * 全局异常处理类
 */
@Slf4j
@ResponseBody
@ControllerAdvice
public class GlobalExceptionHandler {
    
    /**
     * 处理 @Valid和 @Validated注解参数校验失败异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public WebResult exceptionHandler(MethodArgumentNotValidException exception) {
        WebResult webResult = new WebResult();
        webResult.setSuccess(false);
        webResult.setCode(WebHttpCode.SERVICE_ERROR);

        BindingResult bindingResult = exception.getBindingResult();
        if (bindingResult.hasErrors()) {
            //如果有校验错误,返回第一个校验信息
            String defaultMessage = bindingResult.getFieldErrors().get(0).getDefaultMessage();
            webResult.setMessage(defaultMessage);
        }
        return webResult;
    }

}

(2)controller请求方法改造

这里单独使用 @Validated注解。

    @PostMapping("/update2")
    public WebResult update2(@RequestBody @Validated AaUpdateRequest updateRequest) {
        BaseOperateResult operateResult = aaService.update(updateRequest);
        if (!operateResult.isSuccess()) {
            WebResult.error().message(operateResult.getErrorCode().getMessage());
        }
        return WebResult.ok();
    }

测试访问校验ok

3、嵌套使用

@Validated无法单独提供嵌套验证功能。不能用在成员属性上,能配合嵌套验证注解@Valid进行嵌套验证。

(1)请求体

@Data
public class AaUpdate3Request extends BaseRequest {
    private static final long serialVersionUID = 2976273808489796719L;

    @NotNull(message = "id不能为空")
    private Long id;

    @NotEmpty(message = "username不能为空")
    private String username;

    @NotNull(message = "createTime不能为空")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date createTime;

    private Boolean delFlag;
    
    /**
     * 嵌套验证时必须使用 @Valid注解
     */
    @Valid
    @NotNull(message = "文件不能为空")
    private List<AaSaveFileRequest> fileRequestList;

}

(2)controller请求方法

    @PostMapping("/update3")
    public WebResult update3(@RequestBody @Validated AaUpdate3Request update3Request) {
        BaseOperateResult operateResult = aaService.update3(update3Request);
        if (!operateResult.isSuccess()) {
            WebResult.error().message(operateResult.getErrorCode().getMessage());
        }
        return WebResult.ok();
    }

(3)测试访问

在这里插入图片描述

如果想了解 @Validated注解分组校验请查看参考文章。

s三、@Valid 和 @Validated 区别

先看下两者的源码:

@Valid 包位置:javax.validation

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Valid {
}

@Validated 包位置 org.springframework.validation.annotation,是 Spring 做得一个自定义注解,增强了分组功能(对 @Valid的封装);只在 Spring Validator 校验机制使用。

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Validated {
    Class<?>[] value() default {};
}

相同点:

  • @Valid 和 @Validated 两者都可以对数据进行校验,在校验字段上加上规则注解(@NotNull, @NotEmpty等)都可以对 @Valid 和 @Validated 生效。
  • @Valid 和 @Validated 两者都可以与 BindingResult bindingResult配对出现,并且形参顺序是固定的(一前一后),controller对 BindingResult处理返回校验提示。
  • @Valid 和 @Validated 两者也可以单独使用,单独使用当校验不通过时会抛出 BindException异常。这时需要再写一个全局校验异常捕获处理类,然后返回校验提示。

不同点:

  • @Valid可以用在方法、构造函数、方法参数和成员属性(field)上;
  • @Valid可以进行嵌套校验,但是,需要在嵌套的字段上面加上@Valid注解;
  • @Valid不支持分组。
  • @Validated可以用在方法、构造函数、方法参数;但是不能用在成员属性(字段)上;
  • @Validated不支持嵌套校验,因为不能用在成员属性(字段)上;
  • @Validated支持分组验证,以在入参验证时,根据不同的分组采用不同的验证机制;

项目中使用 @Valid和@Validated都可以。如果涉及分组校验时,使用 @Validated注解。

  • @Valid无法单独提供嵌套验证功能。能够用在成员属性上,能配合嵌套验证注解@Valid进行嵌套验证。
  • @Validated无法单独提供嵌套验证功能。不能用在成员属性上,能配合嵌套验证注解@Valid进行嵌套验证。

参考文章:

  • Java代码瘦身,巧用 @Valid,@Validated 的分组校验和嵌套检验,实现高阶参数校验操作:https://blog.csdn.net/weixin_44259720/article/details/127965610

– 求知若饥,虚心若愚。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/513253.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux :进程的程序替换

目录 一、什么是程序替换 1.1程序替换的原理 1.2更改为多进程版本 二、各种exe接口 2.2execlp ​编辑 2.2execv 2.3execle、execve、execvpe 一、什么是程序替换 1.1程序替换的原理 用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往…

python小项目——时钟模拟

钟表是一种计时的装置&#xff0c;也是计量和指示时间的精密仪器。钟表的样式千变万化&#xff0c;但是用来显示时间的表盘相差无几&#xff0c;大多数钟表表盘的样式由刻度&#xff08;共60个&#xff0c;围成圆形&#xff09;、指针&#xff08;时针、分针和秒针&#xff09;…

C++ 11是如何封装Thread库的?

引言 C11 标准引入了一个重要的特性&#xff0c;即原生线程支持&#xff0c;这标志着C语言在并发编程领域迈出了坚实的步伐。在此之前&#xff0c;开发人员在进行跨平台的多线程编程时&#xff0c;不得不依赖于操作系统提供的特定API&#xff0c;如Windows API或POSIX Threads…

RuoYi-Vue若依框架-集成mybatis-plus报错Unknown column ‘search_value‘ in ‘field list‘

报错信息 ### Error querying database. Cause: java.sql.SQLSyntaxErrorException: Unknown column search_value in field list ### The error may exist in com/ruoyi/sales/mapper/ZcSpecificationsMapper.java (best guess) ### The error may involve defaultParameter…

【图像分割】nnUnetV1与V2的Linux部署与应用命令

以前觉得麻烦&#xff0c;一直没用过nnunet&#xff0c;虽然知道它很火&#xff0c;最近一个契机&#xff0c;部署使用了一下nnunet&#xff0c;记录一下其部署和使用的方法与命令。 1、部署 首先&#xff0c;我有一个环境&#xff0c;这个环境可以是以前就有的&#xff0c;也可…

常州小程序案例分享:行业领军企业的数字化转型之路

随着信息技术的快速发展&#xff0c;数字化转型已成为各行各业企业提升竞争力、适应市场变革的关键战略。在江苏省常州市&#xff0c;一些行业领军企业凭借敏锐的市场洞察力和前瞻性的创新思维&#xff0c;成功借助小程序这一轻量化应用平台&#xff0c;实现了业务流程优化、用…

Kubernetes(k8s):如何进行 Kubernetes 集群健康检查?

Kubernetes&#xff08;k8s&#xff09;&#xff1a;如何进行 Kubernetes 集群健康检查&#xff1f; 一、节点健康检查1、使用 kubectl 查看节点状态2、查看节点详细信息3、检查节点资源使用情况 2、Pod 健康检查2.1、 使用 kubectl 查看 Pod 状态2.2、 查看特定 Pod 的详细信息…

基于Springboot+vue的宠物服务管理系统+论文文档

基于Springbootvue的宠物服务管理系统论文文档 预览 简介 本系统共分为三个角色&#xff1a;管理员、用户&#xff1a; 管理员&#xff1a;管理员管理、密码修改、用户管理、充值管理、商品分类管理、商品信息管理、订单信息管理、分享趣事管理、医疗服务管理、服务预约管理…

力扣刷题Days32-92. 反转链表 II(js)

1,题目 给你单链表的头指针 head 和两个整数 left 和 right &#xff0c;其中 left < right 。请你反转从位置 left 到位置 right 的链表节点&#xff0c;返回 反转后的链表 。 2&#xff0c;代码 一次遍历「穿针引线」反转链表&#xff08;头插法&#xff09; /*** Defi…

LeetCode 96. 不同的二叉搜索树

给你一个整数 n &#xff0c;求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种&#xff1f;返回满足题意的二叉搜索树的种数。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;5示例 2&#xff1a; 输入&#xff1a;n 1 输出&#xff1a;1提…

【蓝桥杯嵌入式】六、真题演练(一)-1演练篇:第 14 届真题

温馨提示&#xff1a; 真题演练分为模拟篇和研究篇。本专栏的主要作用是记录我的备赛过程&#xff0c;我打算先自己做一遍&#xff0c;把遇到的问题和不同之处记录到演练篇&#xff0c;然后再返回来仔细研究一下&#xff0c;找到最佳的解题方法记录到研究篇。 目录 解题记录&…

2023年EI会议论文已见刊/检索进展汇总

2023年录用的会议论文已在SPIE、ACM、IEEE等出版社正式上线见刊&#xff0c;并已陆续完成EI Compendex数据库收录&#xff0c;详情如下&#xff1a; EIECT 2023——IEEE出版&#xff0c;并完成EI收录 会议信息&#xff1a; 第三届电子信息工程与计算机技术国际学术会议&…

MapReduce [OSDI‘04] 论文阅读笔记

原论文&#xff1a;MapReduce: Simplified Data Processing on Large Clusters (OSDI’04) 1. Map and Reduce Map&#xff1a;处理键值对&#xff0c;生成一组中间键值对Reduce&#xff1a;合并与同一中间键相关的所有中间值process overview&#xff1a;分割输入数据&#x…

EF数据持久化(三层架构,公司查,改)

效果图 Model设置具体流程在下面链接中 https://blog.csdn.net/Mr_wangzu/article/details/136805824?spm1001.2014.3001.5501 DAL using System; using System.Collections.Generic; using System.Linq; using System.Web; using WebApplication2.Models; namespace WebAppli…

力扣由浅至深 每日一题.20 环形链表

山穷水尽&#xff0c;柳暗花明 —— 24.4.3 环形链表 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整…

实战webSocket压测(一)webSocket背景

一、什么是webSocket&#xff1f; WebSocket是一种在单个TCP连接上进行全双工通信的协议。它允许在客户端&#xff08;如Web浏览器&#xff09;和服务器之间建立持久的连接&#xff0c;实现全双工通信。 二、WebSocket出现的背景 1、http协议背景&#xff1a; 以B/S架构为例…

【数据结构】学会了波兰表达式与逆波兰表达式,怎么能允许自己不会通过计算机进行表达式转换呢?

栈在表达式转换中的应用 导读一、中缀表达式二、表达式的组成部分2.1 单一运算符2.2 不带括号的混合运算符2.3 带括号的混合运算符 三、表达式改写3.1 问题分析3.2 算法设计3.3 算法实现3.4 算法测试 结语 导读 大家好&#xff01;很高兴又和大家见面啦&#xff01;&#xff0…

moveit中RM65-B适配拓展轴一体规划

Moveit适配拓展轴 1.概述 睿尔曼关节模组和机械臂连接时可以被自动识别&#xff0c;并且睿尔曼机械臂提供同时控制机械臂和拓展关节模组的通信协议&#xff08;限一个拓展关节&#xff09;。因此&#xff0c;用户可以在RM机械臂的基础上添加外部关节&#xff0c;构建新的机器…

顶级Layer-3 通证正在飙升,布局龙头Degen Chain(含bitget教程)

近期以太坊生态内&#xff0c;Base 一枝独秀&#xff0c;其 TVL 突破 25 亿美元&#xff0c;创历史新高。并且生态内的社交文化和 DeFi 板块的龙头都很惹眼。 Farcaster 协议上的 meme 币 DEGEN 目前价格为 0.018 美元&#xff0c;7 日涨幅达 376%。 DEGEN 兴起于 Farcaster 的…

备考2024年思维100春季线上比赛?来做做官方模拟题(附答案)

2024年春季思维100活动第一阶段线上比赛&#xff08;4月20日&#xff0c;星期六&#xff0c;上午&#xff09;的报名正在进行中&#xff0c;报名时间截止到为4月6日&#xff08;本周六&#xff09;&#xff0c;请设置好闹钟提醒以免错过。更多安排和需要提前了解的关键点可以见…