spring中使用@Validated,什么是JSR 303数据校验,spring boot中怎么使用数据校验

文章目录

      • 一、JSR 303后台数据校验
        • 1.1 什么是 JSR303?
        • 1.2 为什么使用 JSR 303?
      • 二、Spring Boot 中使用数据校验
        • 2.1 基本注解校验
          • 2.1.1 使用步骤
          • 2.1.2 举例
            • @Valid注解
            • 全局统一异常处理
        • 2.2 分组校验
          • 2.2.1 使用步骤
          • 2.2.2 举例
            • @Validated注解
            • @Validated和@Valid的区别
        • 2.3 自定义校验注解
          • 2.3.1 使用步骤
          • 2.3.2 举例
        • 2.4 相关注解
          • 2.4.1 空检查相关注解
          • 2.4.2 长度检查
          • 2.4.3 布尔值检查
          • 2.4.4 日期检查
          • 2.4.5 数值检查
          • 2.4.6 其他

一、JSR 303后台数据校验

1.1 什么是 JSR303?

JSR 是 Java Specification Requests 的缩写,即 Java 规范提案。存在各种各样的 JSR,简单的理解为 JSR 是一种 Java 标准。JSR 303 是其中数据检验的一个标准(Bean Validation 1.0 (JSR 303))。

1.2 为什么使用 JSR 303?
  • 处理一段业务逻辑,首先要确保数据输入的正确性,所以需要先对数据进行检查,保证数据在语义上的正确性,再根据数据进行下一步的处理。

  • 前端可以通过 js 程序校验数据是否合法,后端同样也需要进行校验。而后端最简单的实现就是直接在业务方法中对数据进行处理,但是不同的业务方法可能会出现同样的校验操作,这样就出现了数据的冗余。

  • 为了解决这个情况,JSR 303 出现了。JSR 303 使用 Bean Validation,即在 Bean 上添加相应的注解,去实现数据校验。这样在执行业务方法前,都会根据注解对数据进行校验,从而减少自定义的校验逻辑,减少代码冗余。

二、Spring Boot 中使用数据校验

2.1 基本注解校验

之前在 Spring MVC 中介绍了数据校验,也例举了常用的注解。但是使注解生效必须要在 springmvc.xml 中配置,假如没有配置文件(比如在 spring boot)中怎么办?----> 下面详细介绍

spring boot 中需要引入依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2.1.1 使用步骤

1、在相关的 Bean 上标注需要处理的注解,并指定需要提示的信息(若不指定,会从默认配置文件中读取默认的信息)。

2、在相关的方法上,使用 @Valid 注解(或者 @Validated 指定组名)标记需要被校验的数据,否则会不生效。

注意:检测到数据异常后,系统会向外抛出异常,如果做了统一异常处理,可以根据 postman 测试的结果,找到控制台打印出的相应的异常,并处理。

3、处理异常。

  • 使用 BindingResult 处理;

  • 也可以使用 全局统一异常 处理(@RestControllerAdvice 与 @ExceptionHandler

    全局统一异常处理后续会讲

2.1.2 举例

1、在相关的 Bean 上标注注解,并写上指定信息。

@Data
public class Emp {@NotNull(message = "id 不能为 null")private Integer id;@NotNull(message = "name 不能为 null")private String name;
}
@Valid注解

2、Controller层中使用 @Valid 注解标记需要检测的数据。

@RestController
public class EmpController {@PostMapping("/emp")public String createEmp(@Valid @RequestBody Emp emp) {return "ok";}
}

3、测试时,假如不传 id、name,会抛出 MethodArgumentNotValidException 异常。使用 BindingResult 可以处理异常信息,但 通常使用统一异常处理

① 使用 BindingResult:

@RestController
public class EmpController {@PostMapping("/emp")public Map createEmp(@Valid @RequestBody Emp emp, BindingResult result) {if (result.hasErrors()) {Map<String, String> map = new HashMap<>();// 获取校验结果,遍历获取捕获到的每个校验结果result.getFieldErrors().forEach(item -> {// 获取校验的信息String message = item.getDefaultMessage(); // 也获取message的值String field = item.getField(); //获取属性名// 存储得到的校验结果map.put(field, message);});return map;}return "ok";}
}

问题:通过上面的步骤,已经可以捕获异常、处理异常,但是每次都是在业务方法中手动处理逻辑,这样的实现,代码肯定会冗余。可以将其抽出,使用 统一异常处理,每次异常发生时,将其捕获。

全局统一异常处理

② 全局统一异常处理:@RestControllerAdvice、@ExceptionHandler

@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(MethodArgumentNotValidException.class)public Map handlerValidException(MethodArgumentNotValidException e) {BindingResult result = e.getBindingResult();Map<String, String> map = new HashMap<>();// 获取校验结果,遍历获取捕获到的每个校验结果result.getFieldErrors().forEach(item ->{// 存储得到的校验结果map.put(item.getField(), item.getDefaultMessage());});return map;}
}

Controller 中不需要再用 BindingResult 去处理数据了。

2.2 分组校验

1、为什么使用 分组校验?

上面的过程,如果出现多个方法,都需要校验 Bean,且校验规则不同的时候,怎么办呢?分组校验就可以去解决该问题,每个分组指定不同的校验规则,不同的方法执行不同的分组,就可以得到不同的校验结果。

2、JSR 303 的每个注解都默认具备三个属性

  • message 用来定义数据校验失败后的提示消息,默认读取配置文件的内容。idea 全局搜索 ValidationMessages.properties,可以看到默认的信息。
  • groups 用来定义分组,它是一个 class 数组,可以指定多个分组。
  • payload()
String message() default "{javax.validation.constraints.NotNull.message}";Class<?>[] groups() default { };Class<? extends Payload>[] payload() default { };
2.2.1 使用步骤

1、定义一个空接口,用于指定分组,内部不需要任何实现。

2、指定 注解时,通过 groups 指定分组。用于指定在某个分组条件下,才去执行校验规则。

3、在 Controller 中通过 @Validated 注解指定分组,去指定校验。

注:使用分组校验后,Bean 注解上若不指定分组,则不会执行校验规则。

2.2.2 举例

1、如:创建两个分组接口 AddGroup、UpdateGroup。

AddGroup 用于指定 添加数据 时的校验规则(比如:id、name 均不为 null)。

UpdateGroup 用于指定 修改数据 时的校验规则(比如:name 不允许为 null)。

2、给 Bean 添加注解,并指定分组信息。

@Data
public class Emp {@NotNull(message = "id 不能为 null", groups = {AddGroup.class})private Integer id;@NotNull(message = "name 不能为 null", groups = {AddGroup.class, UpdateGroup.class})private String name;
}
@Validated注解

3、通过 @Validated 注解指定分组,去指定校验

@RestController
public class EmpController {@PostMapping("/emp")public void createEmp(@Validated({AddGroup.class}) @RequestBody Emp emp) {}@PutMapping("/emp")public void UpdateEmp(@Validated({UpdateGroup.class}) @RequestBody Emp emp) {}
}
@Validated和@Valid的区别
  • @Validated:

    • Spring 框架特有的注解,是标准 JSR-303 的一个变种,提供了一个分组功能。
    • 作用在类上、方法上、方法参数上,不能作用于成员属性上。
  • @Valid:

    • 标准 JSR-303 规范的标记型注解。

    • 作用在方法、构造函数、方法参数、成员属性上。

2.3 自定义校验注解

上面的注解满足不了业务需求时,可以自定义校验注解、然后自定义校验规则

2.3.1 使用步骤

1、自定义一个校验注解。可以创建一个 ValidationMessages.properties 用于保存默认的 message 信息。

2、自定义一个校验器(即自定义校验规则):实现 ConstraintValidator 接口,并重写相关方法。

  • initialize :初始化,可以获取 自定义的属性的值。

  • isValid :校验,可以获取到实际的值,然后与自定义的属性值进行比较。

3、将校验注解 与 校验器 关联起来。@Constraint(validatedBy = {校验器类.class})

2.3.2 举例

自定义一个校验规则,判断数据长度是否合法。

1、自定义一个校验注解:(比如这里是@TestValid)

@Target({FIELD})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy={JiaoYan.class})
public @interface TestValid {// 提示信息String message() default "{本自定义注解的全类名.message}";class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};/*** 返回一个长度* @return 默认为 5*/int length() default 5; 
}

在这里插入图片描述

配置文件内容为:

自定义注解的全类名.message=值不能为 Null,且长度不超过 5

2、自定义一个校验器(比如这里是 JiaoYan)

/*** 实现 ConstraintValidator 接口,* 其中 ConstraintValidator 的泛型,一个需要指定自定义的注解,一个需要指定需要获取的值的类型。* 比如:*  ConstraintValidator<TestValid, String> 中*      TestValid   表示自定义注解*      String      表示获取的值的类型* 即定义规则,判断一个 String 的值的长度是否满足条件*/
public class JiaoYan implements ConstraintValidator<TestValid, String> {/*** 用于保存自定义的(默认)长度*/private int length;/*** 初始化方法,获取默认数据* @param test 注解对象*/@Overridepublic void initialize(TestValid test) {length = test.length();}/*** 自定义校验规则,如果 String 为 Null 或者 长度大于 5,则校验失败(返回 false)* @param value 需要校验的值* @param context* @return true 表示校验成功,false 表示校验失败*/@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {return value == null ? false : length > value.length();}
}

3、使用注解

@Data
public class Emp {@NotNull(message = "id 不能为 null", groups = {AddGroup.class})private Integer id;// 默认@TestValid()@NotNull(message = "name 不能为 null", groups = {AddGroup.class, UpdateGroup.class})private String name;// 自定义@TestValid(length = 10, message = "值不能为 Null 且长度不超过 10", groups = {AddGroup.class})private String email;
}
2.4 相关注解
2.4.1 空检查相关注解
注解注解详情
@Null被指定的注解元素必须为 Null
@NotNull任意类型,不能为 Null,但可以为空,比如:空数组[]、空字符串""
@NotBlank针对字符串,不能为 Null,且去除前后空格后的字符串长度要大于 0
@NotEmpty针对字符串、集合、数组,针对字符串时,不能为 Null,且长度要大于 0
2.4.2 长度检查
注解注解详情
@Size针对字符串、集合、数组,判断长度是否在给定范围内
@Length针对字符串,判断长度是否在给定范围内
2.4.3 布尔值检查
注解注解详情
@AssertTrue针对布尔值,用来判断布尔值是否为 true
@AssertFalse针对布尔值,用来判断布尔值是否为 false
2.4.4 日期检查
注解注解详情
@Past针对日期,用来判断当前日期是否为 过去的日期
@Future针对日期,用来判断当前日期是否为 未来的日期
2.4.5 数值检查
注解注解详情
@Max(value)针对字符串、数值,用来判断是否小于等于某个指定值
@Min(value)针对字符串、数值,用来判断是否大于等于某个指定值
2.4.6 其他
注解注解详情
@Pattern验证字符串是否满足正则表达式
@Email验证字符串是否满足邮件格式
@Url验证是否满足 url 格式
@Digits验证数字整数和小数位数,如:@Digits(integer=6, fraction=2)

文章结束!恭喜你又学会了一个知识点!!!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu常用快捷键和变量记录

alias b‘cd …/’ alias bb‘cd …/…/’ alias bbb‘cd …/…/…/’ alias bbbb‘cd …/…/…/…/’ alias bbbbb‘cd …/…/…/…/…/’ alias bbbbbb‘cd …/…/…/…/…/…/’ alias apkinfo‘aapt dump badging’ alias npp‘notepad-plus-plus’ export ANDROID_HOME/h…

AWS S3文件存储工具类

pom依赖 <!--aws-s3--> <dependency><groupId>com.amazonaws</groupId><artifactId>aws-java-sdk-s3</artifactId><version>1.12.95</version></dependency>S3Utils import cn.hutool.core.util.ZipUtil; import com.a…

【SOC 芯片设计 DFT 学习专栏 -- 测试向量生成 ATPG (Automatic Test Pattern Generation) 】

文章目录 OverviewATPG 的基本功能ATPG 的工作流程ATPG 应用场景示例示例 1&#xff1a;检测单个信号的 Stuck-at Fault示例 2&#xff1a;针对 Transition Fault 的 ATPG ATPG 工具与常用工具链ATPG 优化与挑战 Overview 本文主要介绍 DFT scan 中的 ATPG 功能。在 DFT (Desi…

2024 高通边缘智能创新应用大赛智能边缘计算赛道冠军方案解读

2024 高通边缘智能创新应用大赛聚焦不同细分领域的边缘智能创新应用落地&#xff0c;共设立三大热门领域赛道——工业智能质检赛道、智能边缘计算赛道和智能机器人赛道。本文为智能边缘计算赛道冠军项目《端侧大模型智能翻译机》的开发思路与成果分享。 赛题要求 聚焦边缘智能…

【Python运维】用Python和Ansible实现高效的自动化服务器配置管理

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 随着云计算和大规模数据中心的兴起,自动化配置管理已经成为现代IT运维中不可或缺的一部分。通过自动化,企业可以大幅提高效率,降低人为错…

微信小程序获取后端数据

在小程序中获取后端接口数据 通常可以使用 wx.request 方法&#xff0c;以下是一个基本示例&#xff1a; // pages/index/index.js Page({data: {// 用于存储后端返回的数据resultData: [] },onLoad() {this.fetchData();},fetchData() {wx.request({url: https://your-backe…

应用架构模式-总体思路

采用引导式设计方法&#xff1a;以企业级架构为指导&#xff0c;形成较为齐全的规范指引。在实践中总结重要设计形成决策要点&#xff0c;一个决策要点对应一个设计模式。自底向上总结采用该设计模式的必备条件&#xff0c;将之转化通过简单需求分析就能得到的业务特点&#xf…

【数据结构】双向循环链表的使用

双向循环链表的使用 1.双向循环链表节点设计2.初始化双向循环链表-->定义结构体变量 创建头节点&#xff08;1&#xff09;示例代码&#xff1a;&#xff08;2&#xff09;图示 3.双向循环链表节点头插&#xff08;1&#xff09;示例代码&#xff1a;&#xff08;2&#xff…

【Java设计模式-3】门面模式——简化复杂系统的魔法

在软件开发的世界里&#xff0c;我们常常会遇到复杂的系统&#xff0c;这些系统由多个子系统或模块组成&#xff0c;各个部分之间的交互错综复杂。如果直接让外部系统与这些复杂的子系统进行交互&#xff0c;不仅会让外部系统的代码变得复杂难懂&#xff0c;还会增加系统之间的…

Linux一些问题

修改YUM源 Centos7将yum源更换为国内源保姆级教程_centos使用中科大源-CSDN博客 直接安装包&#xff0c;走链接也行 Index of /7.9.2009/os/x86_64/Packages 直接复制里面的安装包链接&#xff0c;在命令行直接 yum install https://vault.centos.org/7.9.2009/os/x86_64/Pa…

微信小程序 覆盖组件cover-view

wxml 覆盖组件 <video src"../image/1.mp4" controls"{{false}}" event-model"bubble"> <cover-view class"controls"> <cover-view class"play" bind:tap"play"> <cover-image class"…

HTML——57. type和name属性

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>type和name属性</title></head><body><!--1.input元素是最常用的表单控件--><!--2.input元素不仅可以在form标签内使用也可以在form标签外使用-…

uniapp本地加载腾讯X5浏览器内核插件

概述 TbsX5webviewUTS插件封装腾讯x5webview离线内核加载模块&#xff0c;可以把uniapp的浏览器内核直接替换成Android X5 Webview(腾讯TBS)最新内核&#xff0c;提高交互体验和流畅度。 功能说明 下载SDK插件 1.集成x5内核后哪些页面会由x5内核渲染&#xff1f; 所有plus…

力扣hot100——二叉树

94. 二叉树的中序遍历 class Solution { public:vector<int> inorderTraversal(TreeNode* root) {vector<int> ans;stack<TreeNode*> stk;while (root || stk.size()) {while (root) {stk.push(root);root root->left;}auto cur stk.top();stk.pop();a…

设计模式 创建型 单例模式(Singleton Pattern)与 常见技术框架应用 解析

单例模式&#xff08;Singleton Pattern&#xff09;是一种创建型设计模式&#xff0c;旨在确保某个类在应用程序的生命周期内只有一个实例&#xff0c;并提供一个全局访问点来获取该实例。这种设计模式在需要控制资源访问、避免频繁创建和销毁对象的场景中尤为有用。 一、核心…

您的公司需要小型语言模型

当专用模型超越通用模型时 “越大越好”——这个原则在人工智能领域根深蒂固。每个月都有更大的模型诞生&#xff0c;参数越来越多。各家公司甚至为此建设价值100亿美元的AI数据中心。但这是唯一的方向吗&#xff1f; 在NeurIPS 2024大会上&#xff0c;OpenAI联合创始人伊利亚…

uniapp-vue3(下)

关联链接&#xff1a;uniapp-vue3&#xff08;上&#xff09; 文章目录 七、咸虾米壁纸项目实战7.1.咸虾米壁纸项目概述7.2.项目初始化公共目录和设计稿尺寸测量工具7.3.banner海报swiper轮播器7.4.使用swiper的纵向轮播做公告区域7.5.每日推荐滑动scroll-view布局7.6.组件具名…

使用 Python 实现随机中点位移法生成逼真的裂隙面

使用 Python 实现随机中点位移法生成逼真的裂隙面 一、随机中点位移法简介 1. 什么是随机中点位移法&#xff1f;2. 应用领域 二、 Python 代码实现 1. 导入必要的库2. 函数定义&#xff1a;随机中点位移法核心逻辑3. 设置随机数种子4. 初始化二维裂隙面5. 初始化网格的四个顶点…

mysql之组内排序ROW_NUMBER()函数

有个需求&#xff0c;需要组内排序&#xff0c;之前似乎从未接触过此类排序&#xff0c;故查询了一下&#xff0c;记录sql执行结果。 表如下&#xff1a; play_log: 日期 (fdate)用户 ID (user_id)歌曲 ID (song_id)2022-01-081000002022-01-161000002022-01-201000002022-0…

Android TV端弹出的PopupWindow没有获取焦点

在 TV 开发中&#xff0c;焦点管理是通过 Focus Navigation 实现的&#xff0c;PopupWindow 默认不接受焦点&#xff0c;导致遥控器无法选择弹窗内的控件。这是因为 PopupWindow 默认不会将焦点传递到其内容视图上。 要解决问题&#xff0c;可以通过以下步骤调整 PopupWindow …