java后端校验

Java 后端数据校验

一、概述

当我们想提供可靠的 API 接口,对参数的校验,以保证最终数据入库的正确性,是 必不可少 的活。比如下图就是 我们一个项目里 新增一个菜单校验 参数的函数,写了一大堆的 if else 进行校验,非常的不优雅,比起枯燥的 CRUD 来说,参数校验更是枯燥。

这只是一个创建菜单的校验,只需要判断菜单,菜单 url 以及菜单的父类 id 是否为空,上级菜单是否挂载正确,这样已经消耗掉了 30,40 行代码了,更不要说,管理后台这种参数贼多的接口。估计要写几百行校验代码了。

        if(mapper.get("customerId") == null){return RespBean.error("customerId is null!");}if(mapper.get("name") == null){return RespBean.error("name is null!");}if(mapper.get("userName") == null){return RespBean.error("userName is null!");}//查询条件 判断用户名是否重复Map<String,Object> args = new HashMap<>();args.put("userName",mapper.get("userName").toString());args.put("customerId",mapper.get("customerId").toString());Integer findNum = userService.findUserNameIsExist(args);if(!Objects.equals(findNum, 0)){    //查询结果不为0return RespBean.error("用户名重复,请重新输入!");}int ret = userService.addUser(mapper);if(ret >= 0){return RespBean.ok("UserController add user success");}else{return RespBean.error("UserController add User  ailed");}

二、注解

在开始入门之前,我们先了解下本文可能会涉及到的注解。javax.validation.constraints 包下,定义了一系列的约束( constraint )注解。共 22 个,如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HMb3Jaa9-1690357601543)(file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml16688\wps1.jpg)]

大致可以分为以下几类:

2.1 空和非空检查

@NotBlank :只能用于字符串不为 null ,并且字符串 #trim() 以后 length 要大于 0 。

@NotBlank(message = "文本不能为空")
private String text;

@NotEmpty :集合对象的元素不为 0 ,即集合不为空,也可以用于字符串不为 null 。

@NotEmpty(message = "密码不能为空")
private String password;

@NotNull :不能为 null 。

@NotNull(message = "用户名不能为null")
private String userName;

@Null :必须为 null 。

@Null(message = "精度必须为null")
private Double jd;

2.2 数值检查

@DecimalMax(value) :被注释的元素必须是一个数字,其值必须小于等于指定的最大值。

@DecimalMax(value = "50.5",message = "number1必须小于等于50.5")
private Double number1;

@DecimalMin(value) :被注释的元素必须是一个数字,其值必须大于等于指定的最小值。

@DecimalMin(value = "100.6",message = "number2必须大于等于100.6")
private Double number2;

@Max(value) :该字段的值只能小于或等于该值。不支持小数位判断

@Max(value = 100,message = "number7必须小于等于100")
private Long number7;

@Min(value) :该字段的值只能大于或等于该值。 不支持小数位判断

@Min(value = 60,message = "number6必须大于等于60")
private Integer number6;

@Digits(integer, fraction) :被注释的元素必须是一个数字,其值必须在可接受的范围内。

@Digits(integer = 4,fraction = 3,message = "整数位上限4,小数位上限3")
private Double number3;

@Positive :判断正数。

@Positive(message = "number4必须为正数")
private Double number4;

@PositiveOrZero :判断正数或 0 。

@PositiveOrZero(message = "number5必须为正数或0")
private Double number5; 

@Negative :判断负数。

@Negative(message = "number8必须为负数")
private Double number8;

@NegativeOrZero :判断负数或 0 。

@NegativeOrZero(message = "number9必须为负数或0")
private Double number9;

2.3 Boolean 值检查

@AssertFalse :被注释的元素必须为 true 。

@AssertTrue(message = "b1只能为true")
private Boolean b1;

@AssertTrue :被注释的元素必须为 false 。

@AssertFalse(message = "b2只能为false")
private Boolean b2;

2.4 长度检查

@Size(max, min) :检查该字段的 size 是否在 min 和 max 之间,可以是字符串、数组、集合、Map 等。

@Size(max = 5,min = 2,message = "字符串长度在2-5之间")
private String str;

2.5 日期检查

@Future :被注释的元素必须是一个将来的日期。

@Future
private Date date1;

@FutureOrPresent :判断日期是否是将来或现在日期。

@FutureOrPresent
private Date date2;

@Past :检查该字段的日期是在过去。

@Past
private Date date3;

@PastOrPresent :判断日期是否是过去或现在日期。

@PastOrPresent
private Date date4;

2.6 其它检查

@Email :被注释的元素必须是电子邮箱地址。

@Email
private String email;

@Pattern(value) :被注释的元素必须符合指定的正则表达式。注解需要传的参数:一般默认就填入正则表达式正则表达式即可,但是java中字符串需要转义

@Pattern(regexp = "^\\d{15}|\\d{18}$", message = "身份证号码在15-18位")
private String cardNum;

2.7 Hibernate Validator 附加的约束注解

org.hibernate.validator.constraints 包下,定义了一系列的约束( constraint )注解。如下:

@Range(min=, max=) :被注释的元素必须在合适的范围内。只判断整数位数值

@Range(min = 10,max = 20,message = "数值范围在10-20之间")
private Double range;

@Length(min=, max=) :被注释的字符串的大小必须在指定的范围内。

@Length(min = 20,max = 25,message = "字符串长度在20-25之间")
private String str1;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ctYx6pQh-1690357601545)(file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml16688\wps2.jpg)]

2.8 @Valid 和 @Validated

@Valid 注解,是 Bean Validation 所定义,可以添加在普通方法、构造方法、方法参数、方法返回、成员变量上,表示它们需要进行约束校验。

@Validated 注解,是 Spring Validation 锁定义,可以添加在类、方法参数、普通方法上,表示它们需要进行约束校验。同时,@Validated 有 value 属性,支持分组校验。属性如下:

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Validated {/*** Specify one or more validation groups to apply to the validation step* kicked off by this annotation.* <p>JSR-303 defines validation groups as custom annotations which an application declares* for the sole purpose of using them as type-safe group arguments, as implemented in* {@link org.springframework.validation.beanvalidation.SpringValidatorAdapter}.* <p>Other {@link org.springframework.validation.SmartValidator} implementations may* support class arguments in other ways as well.*/Class<?>[] value() default {};}

对于初学的来说,很容易搞混 @Valid(javax.validation 包下) 和 @Validated (org.springframework.validation.annotation 包下)注解。两者大致有以下的区别:

名称Spring validation是否实现了声明式检验是否支持嵌套校验是否支持分组校验
@Validated
@Valid

总的来说,绝大多数场景下,我们使用 @Validated 注解即可。而在有嵌套校验的场景,我们使用 @Valid 注解添加到成员属性上。

三、快速入门

3.1 引入依赖

   <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--在一些高版本springboot中默认并不会引入这个依赖,需要手动引入-->
<!--        <dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator</artifactId><scope>compile</scope></dependency>--><!-- 保证 Spring AOP 相关的依赖包 --><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId></dependency><!--lombok相关 方便开发--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><scope>provided</scope></dependency><!--knife4j接口文档 方便待会进行接口测试--><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>3.0.3</version></dependency></dependencies>

1) @Valid 的使用

首先,需要在实体类的相关字段上添加需要校验的注解。

public class User {private String id;  @NotBlank(message = "密码不能为空")private String password;@Min(value = 18,message = "未成年禁止入内")  private Integer age;
}

其次,在controller层的方法的要校验的参数上添加@Valid注解

@PostMapping("/action/register")public Result registerByForm(@Valid @RequestBody User user){return userService.register(user);}

嵌套功能的使用

在检验 Country对象的 provinces字段时,在provinces字段上添加 @Valid 注解后,就可以检验 list 中的 provinces的属性是否符合要求;

否则只会检验 citys的集合大小是否大于1,不会校验集合中的 citys对象,比如 citys对象的 name 是否符合要求。

    @GetMapping (value = "/test1")public RespBean test1(@RequestBody @Valid Country country) {log.info("country:"+country);return RespBean.sucess();}@Data
public class Country {@NotNull(message = "国家id不能为null")private Long countryId;@Valid@Size(min = 1,max = 2,message = "省份数量在1-2之间")private List<Province> provinces;@Size(min = 1,message = "城市大于1")private List<City> citys;}@Data
public class Province {@NotNull(message = "省份id不能为null")private Long id;@NotBlank(message = "省份名称不能为空")private String name;}@Data
public class City {@NotNull(message = "城市id不能为null")private Long id;@NotBlank(message = "城市名称不能为空")private String name;}

2)@Validated 的使用

@Validated 是 @Valid 的一次封装,在@Valid的基础上增加了分组以及验证排序的功能。

分组功能的使用

当一个实体类需要多种验证方式时,比如:添加时需要对姓名进行非空验证,修改时需要对id进行验证,而添加时就不需要对id进行验证。

首先,定义两个分组的接口:

public interface Add{
}public interface Update{
}

其次,在实体类上使用@Validated的分组功能。

@Data
public class Person {@NotNull(groups = Update.class, message = "更新时候id不能为空")private Long id;@NotEmpty(groups = Add.class, message = "姓名不能为空")private String name;
}在controller中,使用分组进行接口验证。
@RestController
@Slf4j
public class VerifyController {@PostMapping(value = "/validated/add")public void add(@Validated(value = Add.class) @RequestBody Person person) {...}@PostMapping(value = "/validated/update")public void update(@Validated(value = Update.class) @RequestBody Person person) {...}
}

3)定义全局异常处理,才能返回参数校验信息

异常共四种
BindException:处理所有RequestParam注解数据验证异常
MethodArgumentNotValidException:处理所有RequestBody注解参数验证异常
ConstraintViolationException

UnexpectedTypeException

@RestControllerAdvice
@Slf4j
public class GlobalDefaultExceptionHandler {/*** 处理所有RequestBody注解参数验证异常* @param exception* @return*/@ExceptionHandler(MethodArgumentNotValidException.class)public RespBean methodArgumentNotValidException(MethodArgumentNotValidException exception){//封装需要返回的错误信息List<FieldError> fieldErrors = exception.getBindingResult().getFieldErrors();log.error("参数绑定异常,ex = {}", fieldErrors.get(0).getDefaultMessage());return RespBean.error(fieldErrors.get(0).getDefaultMessage());}/*** 处理所有RequestParam注解数据验证异常* @param e* @return*/@ExceptionHandler(BindException.class)public RespBean validationBodyException(BindException e){e.printStackTrace();//打印校验住的所有的错误信息StringBuilder sb = new StringBuilder("参数错误:[");List<ObjectError> list = ((BindException) e).getAllErrors();for (ObjectError item : list) {sb.append(item.getDefaultMessage()).append(',');}sb.deleteCharAt(sb.length()-1);sb.append(']');String msg  =  sb.toString();return RespBean.error(msg);}@ExceptionHandler(UnexpectedTypeException.class)public RespBean unexpectedTypeException(UnexpectedTypeException exception){//封装需要返回的错误信息log.error("参数绑定异常,ex = {}", exception.getMessage());return RespBean.error(exception.getMessage());}@ExceptionHandler(value = ConstraintViolationException.class)public RespBean ConstraintViolationExceptionHandler(ConstraintViolationException ex) {Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();Iterator<ConstraintViolation<?>> iterator = constraintViolations.iterator();List<String> msgList = new ArrayList<>();while (iterator.hasNext()) {ConstraintViolation<?> cvl = iterator.next();msgList.add(cvl.getMessageTemplate());}return RespBean.error(msgList.toString());}
}

补充:分组功能使用多个组检验数据

contoller层使用{}添加多个组别

    @PostMapping(value = "/validated/del")public RespBean dealMore(@Validated(value = {Add.class,Update.class}) @RequestBody Person person) {return RespBean.sucess();}

自定义注解

1-添加自定义注解类接口

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {MobileValidator.class})
public @interface MobileCheck {boolean required() default true;String message() default "手机号码格式有误!";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}

2-实现自定义校验器类

/** 手机号校验器* */
public class MobileValidator implements ConstraintValidator<MobileCheck, String> {private boolean required = false;private static final Pattern mobile_pattern = Pattern.compile("1\\d{10}");public static boolean isMobile(String src){if (StringUtils.isEmpty(src)){return false;}Matcher matcher = mobile_pattern.matcher(src);return matcher.matches();}@Overridepublic void initialize(MobileCheck isMobile) {required = isMobile.required();}@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {if (required) { //如果必须(不能为空),进行校验return isMobile(value);} else {  //如果不必须,非空进行校验if (StringUtils.isEmpty(value)) {return true;}return isMobile(value);}}
}

3-使用

@Data
public class Country {@MobileCheckprivate String phone;}@PostMapping(value = "/checkPhone")
public RespBean checkPhone(@Valid @RequestBody Country country) {return RespBean.sucess();
}

}

@Override
public boolean isValid(String value, ConstraintValidatorContext context) {if (required) { //如果必须(不能为空),进行校验return isMobile(value);} else {  //如果不必须,非空进行校验if (StringUtils.isEmpty(value)) {return true;}return isMobile(value);}
}

}


3-使用

@Data
public class Country {
@MobileCheck
private String phone;

}

@PostMapping(value = “/checkPhone”)
public RespBean checkPhone(@Valid @RequestBody Country country) {
return RespBean.sucess();
}


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

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

相关文章

GPT4ALL私有化部署 01 | Python环境

进入以下链接&#xff1a; https://www.python.org/downloads/release/python-3100/ 滑动到底部 选择你系统对应的版本&#xff0c;如果你是win&#xff0c;那么大概率是win-64bit 有可能你会因为网络的问题导致下载不了&#xff0c;我提供了 链接 接着只需要打开 等待…

单例模式与构造器模式

单例模式 1、是什么 单例模式&#xff08;Singleton Pattern&#xff09;&#xff1a;创建型模式&#xff0c;提供了一种创建对象的最佳方式&#xff0c;这种模式涉及到一个单一的类&#xff0c;该类负责创建自己的对象&#xff0c;同时确保只有单个对象被创建 在应用程序运…

Mac 快速生成树形项目结构目录

我这里使用的是通过包管理 Homebrew安装形式。没有安装的话可以自行搜索 Homebrew 安装方式 brew install tree直接到项目的根目录执行 tree 命令 tree 效果如下&#xff1a; or &#xff1a; tree -CfL 3效果如下&#xff1a;

opencv-24 图像几何变换03-仿射-cv2.warpAffine()

什么是仿射&#xff1f; 仿射变换是指图像可以通过一系列的几何变换来实现平移、旋转等多种操作。该变换能够 保持图像的平直性和平行性。平直性是指图像经过仿射变换后&#xff0c;直线仍然是直线&#xff1b;平行性是指 图像在完成仿射变换后&#xff0c;平行线仍然是平行线。…

基于LNMP配置WordPress建站时出现的问题汇总

这里写目录标题 wordpress上传文件报错问题描述原因分析&#xff1a;解决方案&#xff1a; wordpress裁剪图片报错问题描述原因分析&#xff1a;解决方案&#xff1a; wordpress上传文件报错 WP内部错误&#xff0c;在上传文件时发生了错误&#xff0c;显示权限不足无法保存 …

SQL注入--题目

联合查询注入&#xff1a; bugku-这是一个神奇的登录框 手工注入&#xff1a; 点吧&#xff0c;输入0’发现还是&#xff1a; 输入0" 发现报错&#xff1a; 确定可以注入&#xff0c;判断字段有多少个 0"order by 1,2,3# 发现&#xff1a; 说明有两列。 输入 0&qu…

Docker系列---【docker和docker容器设置开机启动】

docker和docker容器设置开机启动 1、设置docker开机启动 systemctl enable docker 2、设置容器自动重启 1)创建容器时设置 docker run -d --restartalways --name 设置容器名 使用的镜像 (上面命令 --name后面两个参数根据实际情况自行修改)# Docker 容器的重启策略如下&#…

【论文阅读22】Label prompt for multi-label text classification

论文相关 论文标题&#xff1a;Label prompt for multi-label text classification&#xff08;基于提示学习的多标签文本分类&#xff09; 发表时间&#xff1a;2023 领域&#xff1a;多标签文本分类 发表期刊&#xff1a;Applied Intelligence&#xff08;SCI二区&#xff0…

Linux 常用命令全拼

pwd&#xff1a;print work directoryps&#xff1a;process statusdf&#xff1a;disk freedu&#xff1a;disk usagecat&#xff1a;concatenate 连锁&#xff0c;连接insmod&#xff1a;install module 载入模块ln -s&#xff1a;link -soft 创建一个软链接&#xff0c;相当…

9 Linux实操篇-实用指令

9 Linux实操篇-实用指令 文章目录 9 Linux实操篇-实用指令9.1 指定和修改运行级别-init/systemctl9.2 找回root密码9.3 Linux的指令说明9.3 帮助类-man/help9.4 文件目录类-pwd/ls/cd/mkdir/...9.5 时间日期类-date/cal9.6 搜索查找类-find/locate/which/grep9.7 压缩和解压类-…

SpringBoot整合Elasticsearch

SpringBoot整合Elasticsearch SpringBoot整合Elasticsearch有以下几种方式&#xff1a; 使用官方的Elasticsearch Java客户端进行集成 通过添加Elasticsearch Java客户端的依赖&#xff0c;可以直接在Spring Boot应用中使用原生的Elasticsearch API进行操作。参考文档 使用Sp…

【Leetcode】50. Pow(x, n)

一、题目 1、题目描述 实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即, x n x^n xn )。 示例1: 输入:x 

数据库中的事务处理

MySQL的事务处理&#xff1a;只支持 lnnoDB 和BDB数据表类型 1.事务就是将一组SQL语句放在同一批次内去执行 2.如果一个SQL语句出错&#xff0c;则该批次内的所有SQL都将被取消执行 MySQL的事务实现方法一&#xff1a; select autocommit 查询当前事务提交模式 set a…

浅析交互中事实与价值信息的坍缩

在人机交互中&#xff0c;事实与价值的坍缩过程指的是在人与机器智能进行交互时&#xff0c;由于机器智能的回答和信息输出受到编程算法和数据训练的限制&#xff0c;导致人们难以准确区分机器智能提供的信息是基于客观事实还是主观价值观。 以下是人机交互中可能发生的事实与价…

机器学习深度学习——图像分类数据集

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——softmax回归&#xff08;下&#xff09; &#x1f4da;订阅专栏&#xff1a;机器学习&&深度学习…

JavaScript与TypeScript的区别

JavaScript和TypeScript是两种不同的编程语言&#xff0c;在一些方面有一些区别。 1. 类型系统&#xff1a;JavaScript是一种动态类型语言&#xff0c;变量的类型是在运行时确定的&#xff0c;并且可以随时更改。而TypeScript引入了静态类型系统&#xff0c;可以在编译时检查代…

PHP在线相册--【强撸项目】

强撸项目系列总目录在000集 PHP要怎么学–【思维导图知识范围】 文章目录 本系列校训本项目使用技术 上效果图phpStudy 设置导数据库项目目录如图&#xff1a;代码部分&#xff1a;主页 配套资源作业&#xff1a; 本系列校训 用免费公开视频&#xff0c;卷飞培训班哈人&…

【Matlab】基于粒子群优化算法优化BP神经网络的数据回归预测(Excel可直接替换数据)

【Matlab】基于粒子群优化算法优化 BP 神经网络的数据回归预测&#xff08;Excel可直接替换数据&#xff09; 1.模型原理2.数学公式3.文件结构4.Excel数据5.分块代码5.1 fun.m5.2 main.m 6.完整代码6.1 fun.m6.2 main.m 7.运行结果 1.模型原理 基于粒子群优化算法&#xff08;…

国标GB28181协议视频平台EasyCVR修改录像计划等待时间较长的原因排查与解决

音视频流媒体视频平台EasyCVR拓展性强&#xff0c;视频能力丰富&#xff0c;具体可实现视频监控直播、视频轮播、视频录像、云存储、回放与检索、智能告警、服务器集群、语音对讲、云台控制、电子地图、H.265自动转码H.264、平台级联等。为了便于用户二次开发、调用与集成&…

MTK系统启动流程

MTK系统启动流程 boot rom -> preloader ->lk ->kernel ->Native -> Android 1、Boot rom:系统开机&#xff0c;最先执行的是固化在芯片内部的bootrom&#xff0c;其作用主要有 a.初始化ISRAM和EMMC b.当系统全擦后 &#xff0c;也会配置USB&#xff0c;用来仿…