Springboot JSR303校验是怎么回事?

news2024/11/23 15:51:37

概述:

在做项目的过程中,除了要在前端进行数据校验外,服务端也必须做相应的校验,因为高手可绕过前端的校验,直接进入服务端调用相关的方法,进行资料的盗取或破坏。在前端如果使用Vue+ElementUI的方式,ElementUI提供了强大的校验功能,同时也可自定校验,而后端,使用Springboot 框架的项目则提供了JSR303校验,那么什么是JSR303校验?在项目中如何使用?本篇让我进行详细的讲解

一、什么是JSR303校验

  • JSR是Java Specification Requests的缩写,意思是Java 规范提案
  • JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean Validation,即JSR 303
  • Bean Validation规范 ,为Bean验证定义了元数据模型和API.。默认的元数据模型是通过Annotations来描述的,但是也可以使用XML来重载或者扩展。

二、SR303数据校验使用步骤

1、创建Maven工程,在pom.xml文件引入依赖

<!--校验Valid-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
            <version>2.3.2.RELEASE</version>
        </dependency>
        <!--自定义注解-->
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>2.0.1.Final</version>
        </dependency>

2、创建品牌表

CREATE TABLE `kmall_product`.`pro_brand`  (
  `brand_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '品牌id',
  `name` char(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '品牌名',
  `logo` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '品牌logo地址',
  `descript` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '介绍',
  `show_status` tinyint(4) NULL DEFAULT NULL COMMENT '显示状态[0-不显示;1-显示]',
  `first_letter` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '检索首字母',
  `sort` int(11) NULL DEFAULT NULL COMMENT '排序',
  PRIMARY KEY (`brand_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 31 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '品牌' ROW_FORMAT = Dynamic;

3、根据表创建品牌实体类,在类中添加验证规则

  • @NotNull 不为Null 验证,UpdateGroup、UpdateStatusGroup表示在更新时进行不为空的验证
  • @Null为Null 验证,AddGroup表示在添加时进行为空的验证
  • @NotBlank不为空验证,AddGroup、UpdateGroup表示在新增与更新时都要验证
  • @URL地址合法性验证,
  • @ListValue验证值是否在列举的范围内
  • @Pattern自定义正则表达式进行验证
  • @Min最小值验证
@Data
@TableName("pro_brand")
public class BrandEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 品牌id
     */
    @NotNull(message = "修改必须指定品牌id", groups = {UpdateGroup.class, UpdateStatusGroup.class})
    @Null(message = "新增不能指定id", groups = {AddGroup.class})
    @TableId
    private Long brandId;
    /**
     * 品牌名
     */
    @NotBlank(message = "品牌名必须提交", groups = {AddGroup.class, UpdateGroup.class})
    private String name;
    /**
     * 品牌logo地址
     */
    @NotBlank(groups = {AddGroup.class})
    @URL(message = "logo必须是一个合法的url地址", groups = {AddGroup.class, UpdateGroup.class})
    private String logo;
    /**
     * 介绍
     */
    private String descript;
    /**
     * 显示状态[0-不显示;1-显示]
     */
    @NotNull(groups = {AddGroup.class, UpdateStatusGroup.class})
    @ListValue(vals = {0, 1}, groups = {AddGroup.class, UpdateStatusGroup.class})
    private Integer showStatus;
    /**
     * 检索首字母
     */
    @NotEmpty(groups = {AddGroup.class})
    @Pattern(regexp = "^[a-zA-Z]$", message = "检索首字母必须是一个字母", groups = {AddGroup.class, UpdateGroup.class})
    private String firstLetter;
    /**
     * 排序
     */
    @NotNull(groups = {AddGroup.class})
    @Min(value = 0, message = "排序必须大于等于0", groups = {AddGroup.class, UpdateGroup.class})
    private Integer sort;

}

4、在Controller中的方法形参前加上@Valid注解

  • 需要在参数前添加注解: @Validated(AddGroup.class)
    @RequestMapping("/save")
    public R save(@RequestBody @Valid BrandEntity brand, BindingResult result){
        HashMap<String, String> map = new HashMap<>();
        if(result.hasErrors()){
            result.getFieldErrors().forEach((item)->{
                String field = item.getField();
                String message = item.getDefaultMessage();
                map.put(field,message);
            });
            return R.error(400,"提交的数据不合法").put("data",map);
        }else{
            brandService.save(brand);
        }
 
        return R.ok();
    }

三、对校验的错误信息进行统一处理

Springboot提供了@RestControllerAdvice注解,可以对错误信息进行拦截

  • 定义验证错误拦截方法handleValidException
  • 添加注解 @ExceptionHandler(value = MethodArgumentNotValidException.class),表示拦截验证错误信息
  • BindingResult result = e.getBindingResult(); 获取校验错误信息
  • 遍历把错误结构放入errorMap 中
  • 最后将errorMap 信息返回
@Slf4j
@RestControllerAdvice(basePackages = "com.koo.modules.product.controller")
public class MyExceptionControllerAdvice {

    /**
     * 统一处理异常,可以使用Exception.class先打印一下异常类型来确定具体异常
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R handleValidException(MethodArgumentNotValidException e) {
        log.error("数据校验出现问题{}, 异常类型:{}", e.getMessage(), e.getClass());
        BindingResult result = e.getBindingResult();
        Map<String, String> errorMap = new HashMap<>(16);
        // 获取校验的错误结果
        result.getFieldErrors().forEach((item) -> {
            // 获取错误的属性名字 + 获取到错误提示FieldError
            errorMap.put(item.getField(), item.getDefaultMessage());
        });
        return R.error(BizCodeEnume.VALID_EXCEPTION.getCode(), BizCodeEnume.VALID_EXCEPTION.getMsg()).put("data", errorMap);
    }

    @ExceptionHandler(value = Throwable.class)
    public R handleValidException(Throwable throwable) {
        log.error("Throwable错误,未处理:" + throwable);
        return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(), BizCodeEnume.UNKNOW_EXCEPTION.getMsg());
    }

    /**
     * 自定义异常
     * @param e
     * @return
     */
    @ExceptionHandler(value = BusinessException.class)
    public R handleBusinessException(BusinessException e) {
        R r = new R();
        r.put("code", e.getCode());
        r.put("msg", e.getMessage());
        return r;
    }
}

四、分组校验

场景:当我们插入一条数据时,我们可能需要对很多字段进行校验,但在修改时,我们可能只需要对修改的相关字段进行校验,此外,也有可能存在,添加时需要校验而修改不需要校验等情况,所以,我们应该针对不同的操作场景使用不同的校验规则,以此来确保精准校验。

特别注意:

一旦我们在注解@Validated中指定了校验分组,那么对于哪些没有指定校验分组的字段将不再生效。例如下面的这个字段,@NotBlank(message = “logo地址不能为空”) 校验由于没有指定校验分组,而@Validated又指定了Insert分组,所以,该校验无效。

@URL(message = "需要是一个合法的URL",groups = {Insert.class, Update.class})
@NotBlank(message = "logo地址不能为空")
@ApiModelProperty("品牌logo")
private String logo;

1.在校验注解中有个groups属性,里面存放一个接口数组,例如:

@NotNull(message = “修改时品牌id不能为空”,groups = {UpdateGroup.class,AddGroup.class})

/**
	 * 检索首字母
	 */
	@NotEmpty(groups = {AddGroup.class})
	@Pattern(regexp = "^[a-zA-Z]$",message = "检索首字母必须是一个字母",groups = {UpdateGroup.class,AddGroup.class})
	private String firstLetter;
	/**
	 * 排序
	 */
	@NotNull(groups = {AddGroup.class})
	@Positive(message = "排序必须为正整数",groups = {UpdateGroup.class,AddGroup.class})
	private Integer sort;

2、进行了统一异常与分组处理后,可将上面的保存方法精简

@RequestMapping("/save")
    public R save(@Validated(AddGroup.class) @RequestBody BrandEntity brand/*, BindingResult result*/){
        brandService.save(brand);
        return R.ok();
    }

    /**
     * 修改
     */
    @RequestMapping("/update")
    public R update(@Validated({UpdateGroup.class}) @RequestBody BrandEntity brand){
		brandService.updateDetail(brand);
        return R.ok();
    }

3、异常结果:

在这里插入图片描述

五、自定义校验规则

虽然Springboot提供了很多的校验规则,但是不能完全满足项目的实际需要,例如:品牌表中有展示/影藏的字段,限制在[0、1]范围内,不在这个范围的数值插入进来提示“必须提交指定的值”。这个就需要我们自定义校验规则。

根据Springboot校验的源码,我们知道,所有的注解都有三个必须的属性

   String message() default "{com.koo.common.valid.ListValue.message}";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};

1、创建ListValue注解校验

  • @Target 表示可以标注在哪些位置 方法、参数、构造器
  • @Retention(RUNTIME) 可以在什么时候获取到
  • @Constraint 使用哪个校验器进行校验的(这里不指定,在初始化的时候指定)
  • int[] vals() default {}; 指定数据只能是vals数组指定的值
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})// 可以标注在哪些位置 方法、参数、构造器
@Retention(RUNTIME)// 可以在什么时候获取到
@Documented//
@Constraint(validatedBy = {ListValueConstraintValidator.class})// 使用哪个校验器进行校验的(这里不指定,在初始化的时候指定)
public @interface ListValue {
    // 默认会找ValidationMessages.properties
    String message() default "{com.koo.common.valid.ListValue.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    // 可以指定数据只能是vals数组指定的值
    int[] vals() default {};
}

2、添加校验约束

public class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {

    private Set<Integer> set = new HashSet<>();

    /**
     * 初始化方法
     * @param constraintAnnotation
     */
    @Override
    public void initialize(ListValue constraintAnnotation) {
        int[] vals = constraintAnnotation.vals();
        if (vals != null && vals.length != 0) {
            for (int val : vals) {
                set.add(val);
            }
        }
    }

    /**
     * 校验逻辑
     * @param value   需要校验的值
     * @param context 上下文
     * @return
     */
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
        return set.contains(value);// 如果set length==0,会返回false
    }
}

3、在ValidationMessages.properties配置提示信息

valid.com.koo.common.valid.ListValue.message=必须提交指定的值

4、在实体类添加注解

  /**
     * 显示状态[0-不显示;1-显示]
     */
    @NotNull(groups = {AddGroup.class, UpdateStatusGroup.class})
    @ListValue(vals = {0, 1}, groups = {AddGroup.class, UpdateStatusGroup.class})
    private Integer showStatus;
    /**

5、更新状态时添加@Validated({UpdateStatusGroup.class})

 /**
     * 修改状态
     */
    @RequestMapping("/update/status")
    public R updateStatus(@Validated({UpdateStatusGroup.class}) @RequestBody BrandEntity brand){
        brandService.updateById(brand);
        return R.ok();
    }

6、测试

故意设置 showStatus 为2
在这里插入图片描述

源码下载:
https://gitee.com/charlinchenlin/koo-erp

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

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

相关文章

基于SpringBoot3从零配置SpringDoc

为了方便调试&#xff0c;更好的服务于前后端分离式的工作模式&#xff0c;我们给项目引入Swagger。 文章目录 1. SpringFox2. SpringDoc2.1 引入依赖2.2 配置文件2.3 语法2.4 使用示例Tag 用于标识controllerOperation 用于标识方法Schema 用于标识实体类和实体类的属性ApiRes…

Unity Nsight Graphcis 使用

前言 在渲染Profile中&#xff0c;大家经常喜欢使用Renderdoc软件, 之前我的一篇博客也介绍Renderdoc Profile渲染的流程 RenderDoc Debug UE4 Shader_ue4 debug shader_带帯大师兄的博客-CSDN博客 Renderdoc适合查看Draw哪一步出差了&#xff0c;导致效果不符合理想&#xf…

webpack : 无法加载文件 D:\...\node-v18.16.0-win-x64\webpack.ps1,因为在此系统上禁止运行脚本

用idea打开项目时&#xff0c;安装webpack打包的包之后&#xff0c;由于组策略问题拒绝执行脚本 解决方法 1、cmd打开命令行。输入&#xff1a;powershell 出现 PS 证明已经进入组策略模式 2、输入&#xff1a;get-executionpolicy&#xff0c;查看策略 ​ 输入&#xff1a…

浅谈线程池

浅谈线程池 1、线程池 1.1、线程池介绍 线程池是一种多线程处理形式&#xff0c;处理过程中将任务添加到队列&#xff0c;然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小&#xff0c;以默认的优先级运行&#xff0c;并处于多线程…

Golang每日一练(leetDay0057) 缺失区间、最大间距

目录 163. 缺失的区间 Missing Ranges &#x1f31f;&#x1f31f; 164. 最大间距 Maximum Gap &#x1f31f;&#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏…

空闲任务与阻塞延时(笔记)

目录 前言 空闲任务实现空闲任务1、定义空闲任务栈2、定义空闲任务的任务控制块4、定义空闲任务主体 实现阻塞延时vTaskDelay()函数任务与空闲任务切换的例子&#xff1a;vTaskSwitchContext()函数SysTick中断服务函数更新系统时基 SysTick初始化函数实验仿真 前言 软件延时是…

牛客网专项练习Pytnon分析库(二)

1.Z-score标准化公式&#xff0c;,中的σ表示的是什么&#xff08;C&#xff09;。 A.总体平均值 B.数据的方差 C.数据的标准差 D.数据的众数 解析&#xff1a; Z-score标准化也叫标准差标准化法&#xff0c;其中X表示数据样本值&#xff0c;μ表示数据样本的平均值&#x…

婚姻的本质,不是爱情

婚姻的本质&#xff0c;不是爱情 结婚是为了爱情么&#xff1f;普通人或许以为是&#xff0c;但实际并不是。如果你是为了爱情&#xff0c;那你不需要结婚。什么叫爱情。所谓爱情&#xff0c;就是你对她朝思暮想&#xff0c;时时刻刻都想和她在一起。而她也对你朝思暮想&#…

Vue学习笔记1 - Vue是什么?

1&#xff0c;Vue概念 官网上&#xff08;简介 | Vue.js&#xff09; 上说&#xff0c; Vue (发音为 /vjuː/&#xff0c;类似 view) 是一款用于构建用户界面的 JavaScript 框架。 这个还好理解&#xff0c;就是说它是一款前端框架&#xff0c;用于构建 前端界面的。 但是它…

NewBing 还无法访问的几个问题

大部分的AI自媒体都在说&#xff0c;Bing new已经向全世界开放了&#xff0c;我也凑一下这个热闹&#xff0c;用Edge浏览器打开&#xff0c;访问https://www.bing.com/new?ccus 想体验一下Bing new的效果&#xff0c;结果如下&#xff1a; 相信很多人都碰到了这个问题 此体验…

Windows上使用CLion配置OpenCV环境,CMake下载,OpenCV的编译,亲测可用的方法(一)

一、Windows上使用CLion配置OpenCV环境,亲测可用的方法: Windows上使用CLion配置OpenCV环境 教程里的配置: widnows 10 clion 2022.1.1 mingw 8.1.0 opencv 4.5.5 Cmake3.21.1 我自己的配置: widnows 10 clion 2022.2.5 mingw 8.1.0 https://sourceforge.net/projects/min…

二十三种设计模式第三篇--抽象工厂模式

介绍 抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;是围绕一个超级工厂创建其他工厂&#xff0c;该超级工厂又称为其他工厂的工厂。 这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。 在抽象工厂模式中&#xff0c;接口是负责…

对标世界一流|亚马逊供应链管理经验借鉴

当前电商零售行业竞争日趋激烈&#xff0c;服务标准的提升、产品价格的竞争力等因素&#xff0c;导致企业经营成本持续上升&#xff0c;供应链的管理水平已经成为零售行业成败的关键。然而在电商零售行业的红海竞争中&#xff0c;亚马逊却始终保持着高速增长的态势&#xff0c;…

港联证券|4连板的AI+传媒概念股火了,近5亿资金抢筹

今天&#xff0c;沪深两市共51股涨停&#xff0c;除掉10只ST股&#xff0c;合计41股涨停。别的&#xff0c;11股封板未遂&#xff0c;全体封板率为81%。 涨停战场&#xff1a;长江传媒封单量最高 从收盘涨停板封单量来看&#xff0c;长江传媒封单量最高&#xff0c;有39.96万手…

STL初识

什么是STL? 菜鸟教程的解释是&#xff1a;C STL&#xff08;标准模板库&#xff09;是一套功能强大的 C 模板类&#xff0c;提供了通用的模板类和函数&#xff0c;这些模板类和函数可以实现多种流行和常用的算法和数据结构&#xff0c;如向量、链表、队列、栈。 也就是说&am…

4.1 数据结构引入

目录 什么是数据结构 语言出生顺序 基本概念 数据的逻辑结构 数据的存储结构 顺序储存 链式存储 索引存储 散列存储 基本概念 第一卷《基本算法》 第二卷《半数字化算法》 第三卷《排序与搜索》 第四卷《组合算法》 什么是数据结构 数据结构研究计算机数据间关系…

每日学术速递5.5

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CL 1.ResiDual: Transformer with Dual Residual Connections 标题&#xff1a;ResiDual&#xff1a;具有双剩余连接的Transformer 作者&#xff1a;Shufang Xie, Huishuai Zhang, Jun…

制造企业选择库存管理条码工具需要关注哪些点?

Dynamsoft Barcode Reader SDK 一款多功能的条码读取控件&#xff0c;只需要几行代码就可以将条码读取功能嵌入到Web或桌面应用程序。这可以节省数月的开发时间和成本。能支持多种图像文件格式以及从摄像机或扫描仪获取的DIB格式。使用Dynamsoft Barcode Reader SDK&#xff0c…

OpenCV实战(22)——单应性及其应用

OpenCV实战&#xff08;22&#xff09;——单应性及其应用 0. 前言1. 单应性1.1 单应性基础1.2 计算两个图像之间的单应性1.3 完整代码 2. 检测图像中的平面目标2.1 特征匹配2.2 完整代码 小结系列链接 0. 前言 我们已经学习了如何从一组匹配项中计算图像对的基本矩阵。在射影…

读论文《大气压等离子体电离波沿介质管传输特性研究》

文章目录 一、研究背景和意义二、研究目的与内容三、电离波概述3.1 电离波与传统的流注放电3.2 电离波传输速度的计算方法 四、放电参数对电离波传输特性的影响4.1 施加电压与电压波形对电离波传输的影响4.1.1 交流高压对电离波的影响4.1.2 脉冲高压对电离波的影响![在这里插入…