Spring中的数据校验---JSR303

介绍–什么是JSR303

JSR 303是Java中的一项规范,用于定义在Java应用程序中执行数据校验的元数据模型和API。JSR 303的官方名称是"Bean Validation",它提供了一种在Java对象级别上执行验证的方式,通常用于确保输入数据的完整性和准确性。

JSR 303中最常见的用法是使用注解在Java Bean上添加验证规则。以下是一些常用的注解:
此实现与 Hibernate ORM 没有任何关系。 JSR 303 用于对 Java Bean 中的字段的值进行验证。
Spring MVC 3.x 之中也大力支持 JSR-303,可以在控制器中对表单提交的数据方便地验证。
注:可以使用注解的方式进行验证

JSR 303 基本的校验规则

空检查
  • @Null 验证对象是否为null
  • @NotNull 验证对象是否不为null, 无法查检长度为0的字符串
  • @NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0, 只对字符串, 且会去掉前后空格.
  • @NotEmpty 检查约束元素是否为NULL或者是EMPTY.
Booelan检查
  • @AssertTrue 验证 Boolean 对象是否为 true
  • @AssertFalse 验证 Boolean 对象是否为 false
其他校验

@Size(min, max): 检查值的长度是否在指定范围内。
@Min(value): 检查数字值是否大于等于指定值。
@Max(value): 检查数字值是否小于等于指定值。
@Pattern(regex): 使用正则表达式检查字符串值。
@Email: 检查字符串是否为有效的电子邮件地址等。

在实体类或者vo类使用验证规则,可以大幅度减轻数据校验的规范性价比。

具体使用

添加依赖

   <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><dependency><groupId>jakarta.servlet</groupId><artifactId>jakarta.servlet-api</artifactId><scope>provided</scope></dependency>

对需要使用校验的字段添加注解

信息可以进行传值设置,不然就是默认值

@NotEmpty(message = "品牌名必须填写")@Schema(description = "品牌名")private String name;

默认信息如下
在这里插入图片描述

在控制层开启校验

在控制层开启该注解@Validated即可开启校验

   public Result<String> update(@Validated @RequestBody PmsBrandVO vo){pmsBrandService.update(vo);return Result.ok();}

自定义校验失败

上诉的步骤中,校验失败,不满足条件的会抛出异常,所以为了和前端配合需要进行自定义异常处理,返回前端一个json,而不是服务端抛出异常
BindException
在需要返回json的校验bean后跟,BindingResult,校验绑定结果类,对异常进行处理

    public Result<String> save(@Validated({AddGroup.class}) @RequestBody PmsBrandVO vo, BindingResult result){
//
// 形参单个添加这个可以,但是批量很难实现,为此需要自定义异常处理
// 需要取求掉该注解,才可以将异常抛出if (result.hasErrors()){String message = result.getFieldErrors().stream().map(fieldError -> {// 获取到错误字段String field = fieldError.getField();// 获取到自定义的错误消息提示String errMessage = fieldError.getDefaultMessage();// 返回拼接的错误消息字符串return field + ":" + errMessage;}).collect(Collectors.joining(", ")); // 将错误消息用逗号分隔
log.info("错误消息:{}",message);
// 创建一个 Result 对象,将错误消息传递给它return Result.error(message);}else {pmsBrandService.save(vo);return Result.ok();}

但是上诉只样也只是对单个控制器校验校验为此,需要自己,定义异常处理结果

自定义异常处理器 处理校验失败异常

1.把控制层的结构异常结果绑定类进行删除,让控制器将异常进行抛出
2.自定义异常处理

/*** 异常处理器* 用于集中处理所有异常情况,并确保返回给前端的是处理过的 JSON 信息而不是异常信息。*/@Slf4j
@RestControllerAdvice//监听rescontroller的增强方法 advice增强 对应还有controllelrAdvice
public class ServerExceptionHandler {/*** 处理自定义异常* @param ex 抛出的自定义异常* @return 包含异常信息的 Result 对象*/@ExceptionHandler(ServerException.class)//捕获的异常类型public Result<String> handleException(ServerException ex) {return Result.error(ex.getCode(), ex.getMsg());}/*** 处理 Spring MVC 参数绑定、Validator 校验不正确的异常* @param ex 抛出的绑定异常* @return 包含异常信息的 Result 对象*/@ExceptionHandler(BindException.class)public Result<String> bindException(BindException ex) {FieldError fieldError = ex.getFieldError();assert fieldError != null;return Result.error(fieldError.getDefaultMessage());}/*** 处理访问被拒绝的异常* @param ex 抛出的访问被拒绝异常* @return 包含异常信息的 Result 对象*/@ExceptionHandler(AccessDeniedException.class)public Result<String> handleAccessDeniedException(Exception ex) {return Result.error(ErrorCode.FORBIDDEN);}/*** 未知异常类型,用于处理未捕获的其他异常情况*/@ExceptionHandler(Exception.class)public Result<String> handleException(Exception ex) {log.error(ex.getMessage(), ex);return Result.error(ErrorCode.INTERNAL_SERVER_ERROR);}}

这样就可以做到统一处理,异常结果

高级功能

自定义校验注解

如果现有的异常处理结果满足不了我们对字段的需求,那么可以进行自定义校验注解,在自定义校验注解之前,我们需要了解自定义校验注解

自定义注解

注解有些元注解,以及生命周期都是很简单的概念,这里讲一下大致
自定义注解主要通过@interface关键字来定义。
自定义注解的组成包括:

注解声明:使用@interface关键字。
元注解(Meta-annotations):
用于注解其他注解的注解。常用的元注解有@Target、@Retention、- @Documented和@Inherited。

  • @Target:指定注解可以应用的Java元素类型(如METHOD, FIELD等)。
  • @Retention:指定注解在哪一个级别可用,生命周期(源代码中(SOURCE)、类文件中(CLASS)或运行时(RUNTIME)),而我们定义的大部分注解都是在运行时候,用来进行操作日志保存和权限校验
    注解体:定义注解的属性。
    其他的注解关键字,点开任意注解都可以了解大概
演示

我这里定义自定义注解,模拟操作前进行的日志保存
首先启动开启注解功能

@EnableAspectJAutoProxy
/***  所有的Annotation 会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口。*/
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Prelog {String message() default "执行前先打印的日志信息";
}

在我的业务控制器上添加注解
在这里插入图片描述

好了现在我的注解定义好了,并且让我的方法使用上了注解,但是这样注解是没办法知道我们的业务逻辑的,
所以需要实现他的逻辑,这里运用到了aop详细了解aop思想,大概就是对目标做增强,在不改变源码的基础上

引入aop的依赖

 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>

定义一个切面类,对我自定义注解进行逻辑实现

@Aspect
@Component
@Slf4j
public class PrelogAspect {//切点:使用LogAnnotation注解标识的方法都进行切入,也可以使用通配符配置具体要切入的方法名@Pointcut("@annotation(com.mall.Annotation.Prelog)")public void pointCut(){}//环绕通知/*** 在AOP中,joinPoint.proceed()方法用于继续执行切入点处的原始方法。换句话说,它实际上调用了被切入的方法,无论是类的构造函数、方法或字段初始化等。* 在你的情况下,你的切点是使用@Prelog注解标识的方法,因此当切点匹配到一个被@Prelog注解标记的方法时,joinPoint.proceed()方法会执行该方法。因此,在@Around通知中,joinPoint.proceed()执行的就是被@Prelog注解标记的方法。* 所以,Object jsonResult = joinPoint.proceed();这行代码实际上执行了被@Prelog注解标记的方法,并将其结果存储在jsonResult变量中。这个变量可以在切面中进一步处理或返回给调用方。* @param joinPoint* @return* @throws Throwable*/@Around("pointCut()")public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {try {System.out.println("开始执行注解逻辑");// 获取目标方法签名MethodSignature signature = (MethodSignature) joinPoint.getSignature();// 获取目标方法Method method = signature.getMethod();// 获取注解值Prelog annotation = method.getAnnotation(Prelog.class);// 获取属性String message = annotation.message();System.out.println(message);// 执行目标方法Object jsonResult = joinPoint.proceed(); // 执行方法return jsonResult;} catch (Exception e) {e.printStackTrace();throw e; // 抛出异常}}}

访问被标记注解的接口
在这里插入图片描述

当然自定义注解配合aop还可以做权限校验,访问接口前判断是否有对应权限

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;@Aspect
@Component
public class PermissionAspect {@Around("@annotation(CheckPermission)")public Object checkPermission(ProceedingJoinPoint joinPoint) throws Throwable {MethodSignature signature = (MethodSignature) joinPoint.getSignature();CheckPermission annotation = signature.getMethod().getAnnotation(CheckPermission.class);String permission = annotation.value();// 这里模拟权限校验逻辑if (!hasPermission(permission)) {throw new SecurityException("没有权限执行此操作");}return joinPoint.proceed(); // 执行原方法}private boolean hasPermission(String permission) {/***根据该用户在系统的上下文 对照是否拥有该权限**/// 模拟权限校验逻辑,实际中应替换为具体的校验逻辑// 例如,检查当前用户是否拥有该权限// 这里简单模拟总是返回truereturn true;}
}

好了大概了解自定义注解和aop的原理进行实现,自定义校验数据的注解

自定义校验注解实现

1.定义校验注解 我这里定义的是判断字段的值是否是我设置的集合中的值

/*** 1.编写一个自定义注解作用于多个元素校验*/
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {OptionListConstraint.class })//3.指定使用什么校验器 可以指定多个校验器
public @interface OptionList {/*** 1.1改造validate注解的基本属性* 注解的类型只能是基本类型和布尔值以及枚举,字符串* @return*///2.定义自己需要的注解属性int[] values()   default  {0,1};//默认该注解的这个属性是0,1String message() default "必须提交指定的数值";//原min注解的默认消息定义在租界中华这个Class<?>[] groups() default { };
//做校验的时候自定义负载参数Class<? extends Payload>[] payload() default { };
}

2.对注解进行实现
jsr中的注解进行注解校验都是通过实现校验器接口实现的,点开@Constrain注解
在这里插入图片描述

所以需要实现该接口

/*** 2.对校验器进行重写* 实现该接口的俩个方法* 参数<校验注解,校验对象类型>*     就是自定义注解中的逻辑实现*/
public class OptionListConstraint implements ConstraintValidator<OptionList, Integer> {private Set<Integer> set= new HashSet<Integer>();//初始化方法// 参数为校验注解// 可以在该方法中获取校验注解中的属性值@Overridepublic void initialize(OptionList constraintAnnotation) {//1.得到赋于注解的数值int[] values = constraintAnnotation.values();//2.将数值赋值给setfor (int i : values) {set.add(i);}}/*** 判断是否校验成功* @param value object to validate 需要校验的对象 也就是赋值的属性字段的值* @param context context in which the constraint is evaluated 上下文对象** @return*/@Overridepublic boolean isValid(Integer value, ConstraintValidatorContext context) {// 判断当前的值是否在set集合中return set.contains(value);}
}

校验分组

这个行为类似于范围限制,比如,对于id主键字段,我们在新增时候是需要前端不传递的,修改又需要前端传递,为此对与不同状态进行分组处理

  1. 定义俩个接口 表示不同组别 无需写什么方法
    在这里插入图片描述
    2.限定字段校验范围,那些字段是什么组别的时候进行校验
    在这里插入图片描述
    3.控制层 对应校验赋值时标明组别
    在这里插入图片描述

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

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

相关文章

飞链云共创伙伴亮相温州大学,全国首个AIGC大学俱乐部成立!

在这个充满创新活力的时代&#xff0c;我们见证了一个又一个里程碑式的事件。3月3日&#xff0c;温州大学AIGC俱乐部的成立仪式盛大举行&#xff0c;这标志着全国首个AIGC大学俱乐部的诞生。更让人激动的是&#xff0c;飞链云AI共创伙伴——应流&#xff08;广州&#xff09;科…

PFA晶圆夹在半导体芯片制造中的应用

随着半导体技术的不断进步&#xff0c;晶圆制造作为集成电路产业的核心环节&#xff0c;对生产过程的精密性和洁净度要求日益提高。在众多晶圆制造工具中&#xff0c;PFA&#xff08;全氟烷氧基&#xff09;晶圆夹以其独特的材质和性能&#xff0c;在近年来逐渐受到业界的广泛关…

【Datawhale组队学习:Sora原理与技术实战】AIGC技术基础知识

AIGC是什么 AIGC全称叫做AI generated content&#xff0c;AlGC (Al-Generated Content&#xff0c;人工智能生产内容)&#xff0c;是利用AlI自动生产内容的生产方式。 在传统的内容创作领域中&#xff0c;PGC&#xff08;Professionally-generated Content&#xff0c;专业生…

Untiy 使用AVProVideo插件获取视频的长度

AVPro Video是Unity中一个非常流行的视频插件&#xff0c;可在Unity项目中实现高性能的视频播放功能&#xff0c;功能强大且易用。 如图所示&#xff0c;如果我要获取该视频的长度&#xff0c;方法是很简单的。 1、创建一个测试脚本&#xff1a; using RenderHeads.Media.AVP…

vue3+uniapp在微信小程序实现一个2048小游戏

一、效果展示 二、代码 <template><view class"page"><view class"top"><view class"score">得分:{{total}}</view><view class"time">用时:{{allTime}}s</view></view><view cl…

力扣 单词搜索

判断当前单元格(r,c)的字符与字符串待匹配字符word[idx]是否匹配 如果不匹配&#xff0c;直接回退到上一个单元格与字符 如果匹配&#xff0c;搜索相邻单元格与下一个待匹配字符word[idx1] class Solution { public:int dx[5]{0,0,1,-1};int dy[5]{1,-1,0,0};int flag0;bool d…

Https SSL证书 本地化OCSP地址是什么

OCSP地址即SSL证书中的OCSP验签服务器 最近在做的一个项目上线&#xff0c;前线反馈某些地区访问网站显示白屏&#xff0c;直接影响当地用户使用。公司系统是使用公司自研专用的浏览器登录的&#xff0c;是基于早期谷歌浏览器的开源组件开发的&#xff0c;常出现谷歌访问正常而…

DataWorks(ODPS)性能优化技巧指南

使用阿里云DataWorks进行数据处理的时候&#xff0c;有时候会遇到一个sql或pyodps&#xff08;本质上还是转化为sql&#xff09;执行很长的情况&#xff0c;这个时候有必要对代码进行性能优化。 一、打开ODPS运行评估报告 一个sql脚本执行完毕后&#xff0c;在运维中心的周期…

94. 7张图掌握后端服务重构技巧

文章目录 导言一、逻辑模块重构二、存储模块重构三、逻辑与存储模块联合重构总结 原文地址&#xff1a;7张架构图掌握后端服务重构技巧 导言 随着项目发展&#xff0c;现有模块不再符合需求&#xff0c;重构后端服务必要性拉满。&#x1f468;‍&#x1f4bb; 重构过程中&am…

蓝桥杯练习系统(算法训练)ALGO-992 士兵杀敌(二)

资源限制 内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 南将军手下有N个士兵&#xff0c;分别编号1到N&#xff0c;这些士兵的杀敌数都是已知的。   小工是南将军手下的军师&…

数据中心在高性能计算(HPC)中的作用

高性能计算&#xff08;HPC&#xff09;已成为解决复杂问题、推动科学研究、人工智能和其他各种应用领域的关键工具。要确保高性能计算系统的高效运行&#xff0c;需要专门的基础设施和支持。数据中心在满足高密度计算、管理散热和提供强大带宽方面起着关键作用。本文探讨了数据…

langchain学习笔记(十一)

关于langchain中的memory&#xff0c;即对话历史&#xff08;message history&#xff09; 1、 Add message history (memory) | &#x1f99c;️&#x1f517; Langchain RunnableWithMessageHistory&#xff0c;可用于任何的chain中添加对话历史&#xff0c;将以下之一作为…

串的定义及BF算法

定义 BF算法——朴素查找算法——也叫做串的模式匹配算法 其应用特别多&#xff0c;比如经常在一篇文章里面搜索一些东西&#xff0c;&#xff08;比如文章里的某个内容&#xff0c;或某些关键字词出现的位置&#xff0c;次数等&#xff09; 之前我们大多数情况下是用来搜索关…

基于Springboot的助农管理系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的助农管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…

【项目实践】如何解决并发场景下数据一致性问题

1.背景 并发写场景下&#xff0c;由于微服务的系统环境复杂&#xff0c;不可避免的会出现 【机器、缓存、数据库、依赖服务】等出现问题&#xff0c;导致数据不一致的情况产生。 影响大且广&#xff1a; 1、数据不一致问题在线上可能会产生故障 2、数据不一致会导致长尾错误…

IP传输方式——组播

组播作为IP传输三种方式之一&#xff0c;指的是报文从一个源发出&#xff0c;被转发到一组特定的接收者&#xff0c;相同的报文在每条链路上最多有一份。相较于传统的单播和广播&#xff0c;组播可以有效地节约网络带宽、降低网络负载&#xff0c;所以被广泛应用于IPTV、实时数…

项目中如何优雅的使用枚举类型

原文链接&#xff1a;赵侠客 前言 枚举类型在开发中是很常见的&#xff0c;有非常多的应用场景&#xff0c;如状态管理、类型分类、权限控制、配置管理、错误码管理、日志级别等。正确合理的使用枚举可以给我们带来非常多的好处&#xff1a; 增强代码可读性&#xff1a;枚举可…

pandas.DataFrame新增列、dropna()方法-丢弃含空值的行、列;inf的处理技巧

在Dataframe中新添加一列 直接指明列名&#xff0c;然后赋值就可 import pandas as pddata pd.DataFrame(columns[a,b], data[[1,2],[3,4]]) data >>> dataa b 0 1 2 1 3 4 添加一列’c‘&#xff0c;赋值为空白值。打印出来 data[c] data >>>…

1255942-05-2,DBCO-C6-Amine,可以用于构建分子结构和生物活性分子

您好&#xff0c;欢迎来到新研之家 文章关键词&#xff1a;1255942-05-2&#xff0c;DBCO C6 NH2&#xff0c;DBCO-C6-Amine&#xff0c;二苯并环辛炔-C6-氨基 一、基本信息 【产品简介】&#xff1a;DBCO-C6-NH2 is a multifunctional molecule with excellent chemical re…

【王道操作系统】ch1计算机系统概述-05操作系统引导

文章目录 【王道操作系统】ch1计算机系统概述-05操作系统引导01 什么是操作系统引导02 磁盘里边有哪些相关数据&#xff08;1&#xff09;主引导记录&#xff08;MBR&#xff09;&#xff08;2&#xff09;活动分区&#xff08;一般是C盘&#xff09; 03 操作系统引导的过程 【…