Spring Web 嵌套对象校验失效

问题复现

  • 当开发一个学籍管理系统时,我们会提供了一个 API 接口去添加学生的相关信息,学生中有个嵌套属性联系电话,其对象定义参考下面的代码:

    import lombok.Data;
    import javax.validation.constraints.Size;
    @Data
    public class Student {
        @Size(max = 10)
        private String name;
        private short age;
        private Phone phone;
    }
    
    @Data
    class Phone {
        @Size(max = 10)
        private String number;
    }
    
  • 这里我们也给 Phone 对象做了合法性要求(@Size(max = 10)),当我们使用下面的请求(请求 body 携带一个联系电话信息超过 10 位),测试校验会发现这个约束并不生效。

  • 定义完对象后,我们再定义一个 Controller 去使用它,使用方法如下:

    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    @RestController
    @Slf4j
    @Validated
    public class StudentController {
        @RequestMapping(path = "students", method = RequestMethod.POST)
        public void addStudent(@Validated @RequestBody Student student){
            log.info("add new student: {}", student.toString());
            //省略业务代码
        };
    }
    
  • 我们提供了一个支持学生信息添加的接口。启动服务后,使用 IDEA 自带的 HTTP Client 工具来发送下面的请求以添加一个学生:

    POST http://localhost:8080/students
    Content-Type: application/json
    {
      "name": "xiaoming",
      "age": 10,
      "phone": {"number":"12306123061230612306"}
    }
    
  • 发现校验器并没有生效。

案例解析

  • 关于 student 本身的 Phone 类型成员是否校验是在校验过程中(即案例 1 中的代码行 binder.validate(validationHints))决定的。
  • 在校验执行时,首先会根据 Student 的类型定义找出所有的校验点,然后对 Student 对象实例执行校验,这个逻辑过程可以参考代码 ValidatorImpl#validate:
    @Override
    public final <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
       //省略部分非关键代码
       Class<T> rootBeanClass = (Class<T>) object.getClass();
       //获取校验对象类型的“信息”(包含“约束”)
       BeanMetaData<T> rootBeanMetaData = beanMetaDataManager.getBeanMetaData( rootBeanClass );
    
       if ( !rootBeanMetaData.hasConstraints() ) {
          return Collections.emptySet();
       }
    
       //省略部分非关键代码
       //执行校验
       return validateInContext( validationContext, valueContext, validationOrder );
    }
    
  • 这里语句"beanMetaDataManager.getBeanMetaData( rootBeanClass )"根据 Student 类型组装出 BeanMetaData,BeanMetaData 即包含了需要做的校验(即 Constraint)。
  • 在组装 BeanMetaData 过程中,会根据成员字段是否标记了 @Valid 来决定(记录)这个字段以后是否做级联校验,参考代码 AnnotationMetaDataProvider#getCascadingMetaData:
    private CascadingMetaDataBuilder getCascadingMetaData(Type type, AnnotatedElement annotatedElement,
          Map<TypeVariable<?>, CascadingMetaDataBuilder> containerElementTypesCascadingMetaData) {
       return CascadingMetaDataBuilder.annotatedObject( type, annotatedElement.isAnnotationPresent( Valid.class ), containerElementTypesCascadingMetaData,
                   getGroupConversions( annotatedElement ) );
    }
    
  • 在上述代码中"annotatedElement.isAnnotationPresent( Valid.class )"决定了 CascadingMetaDataBuilder#cascading 是否为 true。如果是,则在后续做具体校验时,做级联校验,而级联校验的过程与宿主对象(即 Student)的校验过程大体相同,即先根据对象类型获取定义再来做校验。
  • 在当前案例代码中,phone 字段并没有被 @Valid 标记,所以关于这个字段信息的 cascading 属性肯定是 false,因此在校验 Student 时并不会级联校验它。

问题修正

  • 从源码级别了解了嵌套 Validation 失败的原因后,我们会发现,要让嵌套校验生效,解决的方法只有一种,就是加上 @Valid,修正代码如下:
    @Valid
    private Phone phone;
    
  • 当修正完问题后,我们会发现校验生效了。而如果此时去调试修正后的案例代码,会看到 phone 字段 MetaData 信息中的 cascading 确实为 true 了,参考下图:
    在这里插入图片描述

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

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

相关文章

计算机网络 (27)IP多播

前言 IP多播&#xff08;也称多址广播或组播&#xff09;技术是一种允许一台或多台主机&#xff08;多播源&#xff09;发送单一数据包到多台主机&#xff08;一次性的、同时的&#xff09;的TCP/IP网络技术。 一、基本概念 定义&#xff1a;多播作为一点对多点的通信&#xff…

计算机毕业设计PyHive+Hadoop深圳共享单车预测系统 共享单车数据分析可视化大屏 共享单车爬虫 共享单车数据仓库 机器学习 深度学习

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

口碑很好的国产LDO芯片,有哪些?

在几乎任何一个电路设计中&#xff0c;都可能会使用LDO&#xff08;低压差线性稳压器&#xff09;这个器件。 虽然LDO不是什么高性能的IC&#xff0c;但LDO芯片市场竞争异常激烈。最近几年&#xff0c;诞生了越来越多的精品国产LDO&#xff0c;让人看得眼花缭乱。 业内人士曾经…

Transformer:深度学习的变革力量

深度学习领域的发展日新月异&#xff0c;在自然语言处理&#xff08;NLP&#xff09;、计算机视觉等领域取得了巨大突破。然而&#xff0c;早期的循环神经网络&#xff08;RNN&#xff09;在处理长序列时面临着梯度消失、并行计算能力不足等瓶颈。而 Transformer 的横空出世&am…

低代码从“产品驱动”向“场景驱动”转型,助力数字化平台构建

一、前言 在数字化时代的大潮中&#xff0c;从宏观层面来看&#xff0c;新技术的落地速度不断加快&#xff0c;各行各业的数字化进程呈现出如火如荼的态势。而从微观层面剖析&#xff0c;企业面临着行业格局快速变化、市场竞争日益激烈以及成本压力显著增强等诸多挑战。 据专…

01-51单片机LED与独立按键

一、单片机概述 注意&#xff1a;个人学习笔记&#xff0c;里面涉及到的C语言和进程转换相关的知识在C语言部分已经写了&#xff0c;这里是默认都会的状态学习单片机。 1.什么是单片机 单片机&#xff0c;英文Micro Controller Unit&#xff0c;简称MCU。其内部集成了CPU、R…

腾讯云AI代码助手编程挑战赛-刑说

作品简介 鉴于当代普法力度不够大&#xff0c;这个刑说可以帮助大家更好的普及法律知识 技术架构 采用了全后端分离的架构&#xff0c;前端使用Vue.js&#xff0c;腾讯云的AI服务处理自然语言理解与生成。 实现过程 开发环境、开发流程 系统&#xff1a;win11 开发工具&…

Elasticsearch:聚合操作

这里写目录标题 一、聚合的概述二、聚合的分类1、指标聚合&#xff08;Metric Aggregation&#xff09;2、桶聚合&#xff08;Bucket Aggregation&#xff09;3、管道聚合&#xff08;Pipeline Aggregation&#xff09; 三、ES聚合分析不精准原因分析四、聚合性能优化1、ES聚合…

升级 Spring Boot 3 配置讲解 —— 为何 SpringBoot3 淘汰了 JDK8?

学会这款 &#x1f525;全新设计的 Java 脚手架 &#xff0c;从此面试不再怕&#xff01; 随着 Spring Boot 3 的发布&#xff0c;许多开发者发现了一个重要的变化&#xff1a;Spring Boot 3 不再支持 JDK 8。这一变化引发了不少讨论&#xff0c;尤其是对于那些仍然在使用 JDK …

rhcsa练习(3)

1 、创建文件命令练习&#xff1a; &#xff08; 1 &#xff09; 在 / 目录下创建一个临时目录 test &#xff1b; mkdir /test &#xff08; 2 &#xff09;在临时目录 test 下创建五个文件&#xff0c;文件名分别为 passwd &#xff0c; group &#xff0c; bashrc &#x…

汽车免拆诊断 | 2007款保时捷Carrera S车行驶中发动机冷却液温度报警灯异常点亮

故障现象 一辆2007款保时捷Carrera S车&#xff0c;搭载3.8 L自然吸气发动机&#xff0c;累计行驶里程约为7.8万km。车主反映&#xff0c;车辆行驶一段距离后&#xff0c;组合仪表上的发动机冷却液温度报警灯异常点亮。为此&#xff0c;在其他维修厂已更换过节温器、发动机冷却…

ffmpeg7.0 aac转pcm

#pragma once #define __STDC_CONSTANT_MACROS #define _CRT_SECURE_NO_WARNINGSextern "C" { #include "libavcodec/avcodec.h" }//缓冲区大小&#xff08;缓存5帧数据&#xff09; #define AUDIO_INBUF_SIZE 40960 /*name depthu8 8s16 …

Clisoft SOS设置Server和Project

Clisoft SOS设置Server和Project 一、关于SOS Servers、Clients、Projects和Work Areas 以下三个图是官方文档中介绍的三种情况 图1&#xff1a;带有两个客户端的SOS服务器 图2&#xff1a;使用本地缓存服务器 图3&#xff1a;远程设计团队的缓存服务器 因为SOS软件需要…

Windows 安装 Docker 和 Docker Compose

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall ︱vue3-element-admin︱youlai-boot︱vue-uniapp-template &#x1f33a; 仓库主页&#xff1a; GitCode︱ Gitee ︱ Github &#x1f496; 欢迎点赞 &#x1f44d; 收藏 ⭐评论 …

深入理解plt和got表

前言 plt表和got表是和链接过程相关的表。我们知道&#xff0c;一个可执行文件的生成过程需要经过预处理&#xff0c;编译&#xff0c;汇编&#xff0c;链接四个过程。链接又分为静态链接和动态链接。静态链接是发生在程序执行之前&#xff0c;动态链接是发生在程序执行中。 …

深入学习RocketMQ

参考&#xff1a;RocketMQ从从入门到精通_rocketmq入门到精通-CSDN博客 1、消息的类型 普通消息 顺序消息 延时消息 批量消息 事务消息 2、在java中使用 2.1、pom.xml中加入依赖 <dependency><groupId>org.apache.rocketmq</groupId><artifactId…

renben-openstack-使用操作

管理员操作 (1)上传一个qcow2格式的centos7镜像 (2)管理员------>云主机类型------>创建云主机类型 名称&#xff1a;Centos7 VCPU数量&#xff1a;1 内存&#xff1a; 1024 根磁盘&#xff1a; 10G 其他的默认 点击创建云主机类型即可 界面会显示如下 创建公网络 (1)创建…

电脑硬盘系统迁移及问题处理

一、系统迁移准备 1、确认你的电脑主板是否支持安装两块硬盘,如电脑主板有多个M2硬盘接口,我们将新硬盘安装到主板上,原来的老硬盘安装在第二个接口上,主板只有一个M2接口的话可以使用移动硬盘盒。 2、新硬盘安装好后,我们进入原来的系统,在 此电脑–右键–管理–磁盘管…

PySide6-UI界面设计

导论&#xff1a; PySide6和PyQt都是Python对Qt框架的绑定&#xff0c;允许开发者使用Qt创建平台的GUI应用程序。如果你正在开发商业项目&#xff0c;或者需要使用最新的QT6特性&#xff0c;PySide6是一个更好的选择。如果你更倾向于一个成熟的社区和丰富的资源&#xff0c;Py…

ExplaineR:集成K-means聚类算法的SHAP可解释性分析 | 可视化混淆矩阵、决策曲线、模型评估与各类SHAP图

集成K-means聚类算法的SHAP可解释性分析 加载数据集并训练机器学习模型 SHAP 分析以提取特征对预测的影响 通过混淆矩阵可视化模型性能 决策曲线分析 模型评估&#xff08;多指标和ROC曲线的目视检查&#xff09; 带注释阈值的 ROC 曲线 加载 SHAP 结果以进行下游分析 与…