化繁为简,使用Hibernate Validator实现参数校验(一)

news2025/1/8 5:38:46

目录

前言

环境配置

导入依赖

基础校验

校验注解

参数绑定

@PathVariable

@RequestParam

@RequestBody

@Validated

@Valid

单参校验

对象校验

分组校验

顺序校验


前言

        在之前的悦享校园的开发中使用了SSM框架,由于当时并没有使用参数参数校验工具,方法的入参判断使用了大量的if else语句,代码十分臃肿,因此最近在重构代码时,将框架改为SpringBoot后,引入了Hibernate Validator校验工具对参数进行优雅校验(SSM同样可用)。本文将通过实例来演示如何使用该框架。

环境配置

JDK 1.8

Spring Boot 2.7.12

导入依赖

        <!--SpringBoot 2.3开始,校验包单独组件,需要手动引入 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

基础校验

在正式开始之前,需要先对以下的内容作以了解,方便后续的学习。

校验注解

Hibernate Validator,提供了丰富的校验注解来供我们使用,这里列举一些比较常用的:

注解解释
@Null

验证对象是否为空

@NotNull

验证对象是否为非空

@NotBlank

用于String对象,验证字符串不为空且trim()后长度大于0

@Length

验证对象的长度是否符合

@Size

验证对象长度是否在给定的范围之内(用于Array,Collection,Map,String)

@Min

入参对象的值不能小于该值(用于Number和String)

@Max

入参对象的值不能大于该值(用于Number和String)

@Digits

验证number和string的构成是否合法

@Past

验证date和calendar对象是否在当前时间之前

@Future

验证date和calendar对象是否在当前时间之后

@Pattern

验证是否符合正则表达式规则

@Email验证邮箱
@Positive

验证输入的对象是否非负数

@Range

验证输入的对象的值是否在指定范围内

参数绑定

@PathVariable

该注解用于接收具有Restful风格的参数,如/api/v1/1001,最终userId的值为1001

如下代码中,使用name属性可以指定GetMapping中的id名称与之对应,从而可以自定义参数名称userId,而不是使用默认名称id

@GetMapping("/v1/{id}")
public void getMsg(@PathVariable(name = "id") Integer userId){}

@RequestParam

该注解用于接收查询参数,如/api/v1/product?user="123",则user的值为123。该注解也可用于接收form-data类型的数据。

当在参数前使用@RequireParam时,当请求该方法时,对应的参数必须存在,否则会引发异常,可使用@RequireParam(required = false)指明该参数非必须,该注解在入参为null时可提供默认值。

@GetMapping("/v1/product")
public void getMsg(@RequestParam String user){}

@RequestBody

该注解用于接收JSON格式的数据,如请求为{"name":123,"age":18},需要有对应的实体类作为映射。

@PostMapping("/v1/user")
public void getMsg(@RequestBody User user){}

@Validated

该注解可以作用于类、方法、参数上,用于开启参数校验。

@Valid

该注解可以作用于方法、参数、字段、构造器上,同样可以用于参数校验。

单参校验

将@Validated注解用于类上,可以不用在每个方法的参数上重复开启校验,使用前面提的基础校验即可对参数作约束。 

校验方法

@RestController
@RequestMapping("/v1/hello")
@Slf4j
@Validated
public class HelloController {

    /**
     * GetMapping 请求
     * @param name 用户名称
     * @param uid  用户uid
     * @return
     */
    @GetMapping("/{id}")
    public ResultDataVO getMsg(@RequestParam String name,
                               @Max(value = 10,message = "最大值不能超过10")
                               @PathVariable(name = "id") int uid) {
        log.info("访问hello下的msg方法。");
        String result = "Hello,"+name+" id "+uid;

        return ResultDataVO.success(result);
    }
}

请求参数 响应结果

 由于我们没有给String参数赋值,因此默认为空字符串。但由于在uid上使用了@Max注解,因此当入参为100时便会出现异常,而返回的JSON格式为全局的响应处理,在后续的文章会提到。

对象校验

当我们在使用中,往往会遇到多个参数的情况,由于get请求对参数长度有限制,且参数会拼接在URL中, 因此对于此场景我们一般使用对象的方式来接收参数,而对于我们的参数也将传入也将用JSON格式。

由于使用JSON格式传参,将使用@RequestBody注解,除此之外还要使用@Validated或@Valid注解,关于两者的选择,只需要记住分组校验使用@Validated,否则二者均可。

即使在类上添加了@Validated注解,此处仍然需要以上两个注解之一,否则参数校验将无效。

校验方法

@RestController
@RequestMapping("/v1/hello")
@Slf4j
@Validated
public class HelloController {

    /**
     * 对象校验示例
     * @param user
     * @return 
     */
    @PostMapping("/msg")
    public ResultDataVO(@RequestBody @Valid UserDTO user){
        return ResultDataVO.success(user.toString());
    }
}

UserDTO

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserDTO {

    /**
     * id
     */
    @Min(value = 1,message = "id值不合法")
    @Positive
    private Integer id;
    /**
     * 昵称
     */
    @NotBlank(message = "用户名不能为空")
    @Length(min = 4,max=10,message = "用户名必须在4-10个字符之间")
    private String username;
    /**
     * 年龄
     */
    @Range(min = 1,max=120,message = "年龄不在合法范围内")
    @NotNull(message = "age不能为空")
    private Integer age;
}

上述代码中,参数为UserDTO对象,其中包含三个待校验的字段,由于未涉及到分组校验,因此校验方法中参数前使用@Valid注解(@Validated注解亦可),注意入参的字段名称需要和UserDTO中的名称一致。

请求参数

 响应结果

分组校验

假设有这样的场景,在用户登录和修改密码中,两者分别需要如下参数,登录时需要用户名、密码,而修改时需要密码、新密码,为了方便管理,只使用了一个DTO类来存放上述的字段,此时由于这些参数需要在不同的场景下校验,而加上基础校验的注解会导致所有参数均被校验,面对此问题,我们将使用分组校验来解决。

可以看到如何代码中,均是使用了@Validate注解,且注解中含有一个分组接口,使用该接口可以帮助我们告知程序该对哪些参数进行校验。

校验方法

@RestController
@Slf4j
@RequestMapping("/local/auth")
@Validated
public class LocalAuthController {

    @Autowired
    private LocalAuthService localAuthService;

    /**
     * 用户登录接口
     * @param localAuthDTO 账号对象
     * @return
     */
    @PostMapping("/login")
    LocalAuthVO loginCheck(@RequestBody @Validated(AuthCheckSequence.class) LocalAuthDTO localAuthDTO){
        return localAuthService.userLoginCheck(localAuthDTO);

    }

    /**
     * 用户修改密码接口
     * @param localAuthDTO 账号对象
     * @return
     */
    @PutMapping("/password")
    ResultDataVO userChangePassword(@RequestBody
                                    @Validated(AuthChangeSequence.class)
                                    LocalAuthDTO localAuthDTO){
        return localAuthService.userModifyPassword(localAuthDTO);
    }
}

分组接口

AuthChangeSequence 

/**
 * 分组校验,用户登录
 */

public interface AuthCheckSequence {

}

AuthCheckSequence 

/**
 * 分组校验,密码修改
 */
public interface AuthChangeSequence {

}

LocalAuthDTO 

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class LocalAuthDTO {

    /**
     * 用户名
     */
    @NotBlank(message = "用户名不能为空",groups = AuthCheckSequence.class)
    @Pattern(regexp = "^[a-zA-Z0-9]{3,8}$", message = "用户名必须由3-8个字母或数字组成",
    groups = AuthCheckSequence.UsernameCheck.class)
    private String username;

    /**
     * 密码
     */
    @NotBlank(message = "密码不能为空",groups = AuthCheckSequence.class)
    @Size(min = 6,max = 18, message = "密码长度必须在6到18位之间",groups = AuthCheckSequence.PasswordCheck.class)
    private String password;

    /**
     * 新密码,修改密码时使用
     */
    @NotBlank(message = "新密码不能为空",groups = AuthChangeSequence.class)
    @Size(min = 6,max = 18, message = "新密码长度必须在6到18位之间",groups = AuthChangeSequence.ChangePassword.class)
    private String newPassword;
}

请求参数

响应结果

从上图所示请求结果可以看出,使用@Validated注解+分组校验接口后,将只对该参数中标明了对应分组接口的字段进行校验,并不会对全部参数进行校验。 

顺序校验

由于用户人数增多,我们将在对用户进行分组,将在LocalAuthDTO中引入新的字段,而我们所期望的是能够按照用户类型,用户名,密码的顺序来校验,而不是像之前对象校验最终显示的结果那样,随机进行校验并返回结果,因此需要使用在分组校验的基础上做一些改进,使其可以按照我们所指定的顺序执行校验,以前面登录为例:

校验方法

@RestController
@Slf4j
@RequestMapping("/local/auth")
@Validated
public class LocalAuthController {

    @Autowired
    private LocalAuthService localAuthService;

    /**
     * 用户登录接口
     * @param localAuthDTO 账号对象
     * @return
     */
    @PostMapping("/login")
    LocalAuthVO loginCheck(@RequestBody @Validated(AuthCheckSequence.class) LocalAuthDTO localAuthDTO){
        return localAuthService.userLoginCheck(localAuthDTO);

    }
}

 AuthCheckSequence

通过在该分组接口中新建接口,并使用@GroupSequence注解中参数的顺序来实现校验参数的顺序。如下将标识使用AuthCheckSequence分组的参数将按照用户类型、用户名、密码的顺序进行校验。

@GroupSequence({AuthCheckSequence.UserTypeCheck.class, AuthCheckSequence.UsernameCheck.class, AuthCheckSequence.PasswordCheck.class})
public interface AuthCheckSequence {

    /**
     * 用户类型
     */
    interface UserTypeCheck {};

    /**
     * 用户名校验
     */
    interface UsernameCheck {};

    /**
     * 密码校验
     */
    interface PasswordCheck {};
}

LocalAuthDTO

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class LocalAuthDTO {

     /**
     * 用户类型 1.用户 2.商家 3.管理员,目前仅支持用户和商家类型注册
     */
    @NotNull(message = "用户类型不能为空",groups = AuthCheckSequence.UserTypeCheck.class)
    @Range(min = 1,max = 3,message = "用户类型不合法",groups =         
 AuthCheckSequence.UserTypeCheck.class)
    private Integer userType;

    /**
     * 用户名
     */
    @NotBlank(message = "用户名不能为空",groups = AuthCheckSequence.UsernameCheck.class)
    @Pattern(regexp = "^[a-zA-Z0-9]{3,8}$", message = "用户名必须由3-8个字母或数字组成",
    groups = AuthCheckSequence.UsernameCheck.class)
    private String username;

    /**
     * 密码
     */
    @NotBlank(message = "密码不能为空",groups = AuthCheckSequence.PasswordCheck.class)
    @Size(min = 6,max = 18, message = "密码长度必须在6到18位之间",groups = AuthCheckSequence.PasswordCheck.class)
    private String password;
}

请求参数

 响应结果

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

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

相关文章

如何向学校图书馆推荐数据库?

学校图书馆在满足师生学习和研究需求方面起着至关重要的作用。作为一名学生或研究员&#xff0c;您可能会意识到某些数据库对于学校的教学和研究至关重要。遇到拥有优质数据的数据库如何向学校图书馆推荐呢&#xff1f; 一、了解学校需求。 在向学校图书馆推荐数据库之前&…

WMS精细化库存管理

在现代供应链管理中&#xff0c;仓储和库存管理起着至关重要的作用。仓库管理系统&#xff08;WMS&#xff09;作为一种关键工具&#xff0c;通过精细化库存管理&#xff0c;提高了仓库操作的效率和准确性。 一、概述WMS精细化库存管理 WMS精细化库存管理是指利用先进的信息技…

【1++的数据结构】之map与set(一)

&#x1f44d;作者主页&#xff1a;进击的1 &#x1f929; 专栏链接&#xff1a;【1的数据结构】 文章目录 一&#xff0c;关联式容器与键值对二&#xff0c;setset的使用 三&#xff0c;mapmap的使用 四&#xff0c;multiset与multimap 一&#xff0c;关联式容器与键值对 像l…

linux文本三剑客

linux文本三剑客 1、grep2、sed 1、grep 过滤 参数用法作用-igrep -i STRING xxx.txt从xxx.txt文件查找不区分大小写STRING-wgrep -w STRING xxx.txt精确匹配STRING-egrep -e STRING1 -e STRING2 xxx.txt查找多个STRING行-ngrep -n STRING xxx.txt查看STRING 在第几行-vgrep …

亚马逊秒杀有什么作用

亚马逊的秒杀活动具有多方面的作用&#xff0c;对于消费者、亚马逊自身以及卖家来说都有一定的影响和作用&#xff1a; 1、吸引消费者和增加销售额&#xff1a;秒杀活动的低价优惠通常会吸引大量的消费者&#xff0c;促使他们在限定的时间内购买商品。这有助于增加销售额和订单…

React+Typescript从请求数据到列表渲染

我们在项目src目录下创建一个目录 叫 pages 在里面创建一个组件叫 list.tsx 这里 我启动了自己的java项目 创建接口 你们就也需要弄几个自己的接口做测试 然后 list.tsx 编写代码如下 import * as React from "react";export default class hello extends React.C…

空调企业只干空调,卡萨帝却干了业主想不到的事

作者 | 曾响铃 文 | 响铃说 今年入夏以来&#xff0c;随着气温的不断攀升&#xff0c;空调已经成为生活、工作中的“绝对刚需”。由此而来的则是空调产品的销量大增。 但也有不少消费者表示&#xff0c;随着产品功能的越发相似&#xff0c;价格趋同&#xff0c;使空调变得越…

前端显示gif流文件,gif图验证码

1、前端界面展示效果图&#xff1a;gif动态图片 2、接口获取流文件 3、接口配置 export function getBlob(params){return request({url: /session/generatorCode,method: get,params,catchAll:true,responseType:"arraybuffer"}) }4、数据处理 getBlob({key:thi…

【微服务】spring 条件注解从使用到源码分析详解

目录 一、前言 二、spring 条件注解概述 2.1 条件注解Conditional介绍 2.2 Conditional扩展注解 2.2.1 Conditional扩展注解汇总 三、spring 条件注解案例演示 3.1 ConditionalOnBean 3.2 ConditionalOnMissingBean 3.2.1 使用在类上 3.2.2 使用场景补充 3.3 Condit…

The last packet sent successfully to the server was 0 milliseconds ago.报错

报错图&#xff1a; 解决方法 多半是代码拼写错误&#xff0c;如localhost拼成local。 注意驱动版本应与mysql版本对应 maven包配置按照自己下的位置设置&#xff0c;最好不要默认

成都车展亮相:专为亲子家庭打造,极狐考拉纯电智能亲子车

极狐汽车旗下的考拉品牌于今年年初面向拥有婴幼儿的中产阶级宝爸宝妈推出&#xff0c;旨在满足他们对于安全、健康和便利的高要求。这款名为“考拉”的纯电智能亲子车将于2023年8月25日在成都车展上开启预售。 外观方面&#xff0c;考拉的造型采用流线型的车身设计&#xff0c;…

[网络架构]Self-organized operational neural networks (SelfONN)

Self-organized operational neural networks (SelfONN 背景CNNONNSelfONNCNN, ONN&#xff0c; SelfONN对比SelfONN与CNN的关系总结References 背景 本节要分享的是SelfONN, SelfONN可以看作是ONN的优化/升级&#xff0c; 而ONN可以看作是更一般化的CNN&#xff0c; 克服了CN…

什么是神经网络

什么是神经网络 什么是神经网络&#xff1f;CNN、RNN、GNN&#xff0c;这么多的神经网络&#xff0c;有什么区别和联系&#xff1f; 既然我们的目标是打造人工智能&#xff0c;拥有智慧的大脑无疑是最好的模仿对象&#xff0c;人脑中有约860亿个神经元&#xff0c;这被认为是…

运维高级学习--Docker(二)

1、使用mysql:5.6和 owncloud 镜像&#xff0c;构建一个个人网盘。 #拉取mysql5.6和owncloud镜像 [rootlocalhost ~]# docker pull mysql:5.6 [rootlocalhost ~]# docker pull owncloud [rootlocalhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED …

【FAQ】H.265视频无插件流媒体播放器EasyPlayer.js播放webrtc断流重连的异常修复

H5无插件流媒体播放器EasyPlayer属于一款高效、精炼、稳定且免费的流媒体播放器&#xff0c;可支持多种流媒体协议播放&#xff0c;可支持H.264与H.265编码格式&#xff0c;性能稳定、播放流畅&#xff0c;能支持WebSocket-FLV、HTTP-FLV&#xff0c;HLS&#xff08;m3u8&#…

Rider 添加NuGet软件包 (NuGet Package)

如图&#xff0c;在解决方案中选择自己的项目右键&#xff0c;点击管理 NuGet 软件包即可 在搜索栏中搜索自己要使用的软件包安装即可使用

K8S如何部署Redis(单机、集群)

在今天的讨论中&#xff0c;我们将深入研究如何将Redis数据库迁移到云端&#xff0c;以便更好地利用云计算的优势提高数据管理的灵活性。 Redis(Remote Dictionary Server)是一个开源的、基于内存的数据结构存储系统&#xff0c;它可以用作数据库、缓存和消息代理。Redis支持多…

MES生产报工管理

一、MES生产报工管理的定义与功能&#xff1a; MES生产报工管理是指利用制造执行系统&#xff08;MES&#xff09;对生产过程进行实时监控、数据采集和分析&#xff0c;并及时记录和报告生产工单的实际完成情况。其主要功能包括&#xff1a; 1. 实时数据采集&#xff1a;通过…

如何在MT4安装和设置ADX指标,3步完成

ADX是MT4和MT5中的基本指标之一&#xff0c;但是很多投资者都不知道如何在MT4安装和设置ADX指标&#xff0c;其实很简单&#xff0c;跟着anzo capital昂首资本3步完成设置。 第一步&#xff1a;在终端中打开货币对报价图表&#xff0c;并创建一个新的图形。可以通过单击“文件”…

自定义滑动到底部触发指令,elementUI实现分页下拉框

在 main.js 中添加 // 自定义滑动到底部指令 Vue.directive(selectLoadMore, {bind(el, binding) {// 获取element-ui定义好的scroll盒子const SELECTWRAP_DOM el.querySelector(.el-select-dropdown .el-select-dropdown__wrap)SELECTWRAP_DOM.addEventListener(scroll, fun…