千家信息网

SpringBoot参数如何校验

发表于:2025-11-07 作者:千家信息网编辑
千家信息网最后更新 2025年11月07日,本文小编为大家详细介绍"SpringBoot参数如何校验",内容详细,步骤清晰,细节处理妥当,希望这篇"SpringBoot参数如何校验"文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习
千家信息网最后更新 2025年11月07日SpringBoot参数如何校验

本文小编为大家详细介绍"SpringBoot参数如何校验",内容详细,步骤清晰,细节处理妥当,希望这篇"SpringBoot参数如何校验"文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

一、前言

在 Web 开发中经常需要对前端传过来的参数进行校验,例如格式校验、非空校验等,基本上每个接口都需要进行校验。如果使用常规的 IF ELSE 进行校验,随着参数越来越多,校验逻辑的冗余度也越来越高,导致维护性变差。在 Java 中定义了一套基于注解的数据校验规范 Bean Validation ,通过一些简单的注解就能完成必要的逻辑校验,相对来说就方便了很多。而 Bean Validation 只是规范,并没有具体的实现,Hibernate 提供了具体的实现,也即 Hibernate Validator ,这个也是目前使用得比较多的验证器了。

二、注解介绍

validator 内置注解

  • @Null 被注释的元素必须为 null

  • @NotNull 被注释的元素必须不为 null

  • @AssertTrue 被注释的元素必须为 true

  • @AssertFalse 被注释的元素必须为 false

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

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

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

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

  • @Size(max, min) 被注释的元素的大小必须在指定的范围内

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

  • @Past 被注释的元素必须是一个过去的日期

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

  • @Pattern(value) 被注释的元素必须符合指定的正则表达式

Hibernate Validator 附加的 constraint

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

  • @Length 被注释的字符串的大小必须在指定的范围内

  • @NotEmpty 被注释的字符串的必须非空

  • @Range 被注释的元素必须在合适的范围内

  • @NotBlank 验证字符串非 null ,且长度必须大于0

注意

  • @NotNull 用于验证对象是否不为 null ,无法检测长度为0的字符串;

  • @NotEmpty 用于 String、Map 或者数组等集合类型不能为 null 且长度必须大于0;

  • @NotBlank 只能用于String,不能为 null ,且调用 trim() 后,长度必须大于0;

校验字符串是否为空,使用 @NotNull ,只有参数不传的时候才会检测到,传了空值(例如空字符串)仍然可以通过校验,因此应该使用 @NotBlank

三、添加依赖

SpringBoot 中 Bean Validation 已经集成在 starter-web 中,因此无需再添加依赖。但是本人实际测试发现,直接使用好像不行,因此添加了如下依赖:

        org.springframework.boot        spring-boot-starter-validation

四、创建用于校验的实体类

创建一个 validator 目录,在里面创建一个 UserDTO

本来想直接复用之前创建的 entity 类,但是后来想了下,entity 用于建立数据库的映射关系,字段跟数据表是一一对应的,而这里的 validator 是用于校验前端传过来的参数,字段跟前端传的参数是对应的,因此不能复用,需要单独写一个 validator 类。

顺便提一下,在 RestController 中使用自己定义的对象,需要有 setter、getter 之类的方法,或者使用 lombok 的 @Data 注解。如果不加的话会报错:

No converter found for return value of type: class validator.UserDTO

使用 getter、setter 方法如下:

public class UserDTO {        private String username;        private Integer age;        public String getUsername() {                return username;        }        public void setUsername(String username) {                this.username = username;        }        public Integer getAge() {                return age;        }        public void setAge(Integer age) {                this.age = age;        }}

使用 lombok 的 @Data 注解如下,代码与上面等效:

@Datapublic class UserDTO {        private String username;        private Integer age;}

五、写一个测试用的接口

添加一个 POST 接口,从请求体中获取参数,然后原封不动返回过去(主要是用来测试参数校验的,这里接口逻辑并不重要)

@PostMapping("validateUser")public UserDTO userValidate(@RequestBody UserDTO userDTO) {    return userDTO;}

六、在实体类中添加注解

给需要校验的参数添加注解:

@Datapublic class UserDTO {    @NotBlank(message = "用户名不能为空")    private String username;    @NotBlank(message = "手机号不能为空")    private String mobile;    @NotNull(message = "性别不能为空")    private Integer sex;    @NotNull(message = "年龄不能为空")    private Integer age;    @NotBlank(message = "邮箱不能为空")    @Email(message = "邮箱格式错误")    private String email;}

七、在 controller 方法中添加 Validated 注解

然后需要在 controller 方法体添加 @Validated ,不加 @Validated 校验会不起作用。

用下面的数据测试一下:

{    "username": "",    "mobile": "2333",    "sex": 0,    "age": 0,    "email": "233@dby.com"}

可以看到校验是成功了,但是后台抛了一个异常:

Validation failed for argument [0] in public validator.UserDTO com.hhlnyfz.hhlnyfz.HelloController.userValidate(validator.UserDTO): [Field error in object 'userDTO' on field 'username': rejected value []; codes [NotBlank.userDTO.username,NotBlank.username,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userDTO.username,username]; arguments []; default message [username]]; default message [用户名不能为空]] ]

然后返回参数并不理想,前端也并不容易处理返回参数

八、添加全局异常处理

上面这种情况需要添加一下全局异常处理,这样比较规范。创建一个 GlobalExceptionHandler 类,在类的上面添加 @RestControllerAdvice 注解,然后添加如下代码:

/** * 全局异常处理类 */@RestControllerAdvicepublic class GlobalExceptionHandler {    // 捕获 MethodArgumentNotValidException 异常    @ExceptionHandler(value = MethodArgumentNotValidException.class)    public HashMap handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {        HashMap map = new HashMap<>();        map.put("code", 400);        map.put("msg", e.getMessage());        map.put("url", request.getRequestURL());        return map;    }    // 其他异常处理方法}

这边 @ExceptionHandler 注解中的 MethodArgumentNotValidException.class 用于捕获请求参数异常。如果是 Exception.class 表示捕获全部异常。不要用一个方法处理所有的异常,而是一个方法处理一种异常。如果需要处理其他异常,可以在下面添加方法。

还是用刚才的测试用例,这次异常被捕获到了

可以看到 e.getMessage() 把整个错误堆栈信息全部打印出来了,但我们只需要把最后的 default message 返回给前端就行,因此改用 e.getBindingResult().getFieldError().getDefaultMessage() ,然后 IDE 给了提示:

Method invocation 'getDefaultMessage' may produce 'NullPointerException'

也就是说 e.getBindingResult().getFieldError() 可能会是一个空指针 null ,于是按照 IDE 的提示用 Objects.requireNonNull 包裹一下,最终代码如下:

/** * 全局异常处理类 */@RestControllerAdvicepublic class GlobalExceptionHandler {    // 捕获 MethodArgumentNotValidException 异常    @ExceptionHandler(value = MethodArgumentNotValidException.class)    public HashMap handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {        HashMap map = new HashMap<>();        map.put("code", 400);        map.put("msg", Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage());        map.put("url", request.getRequestURL());        return map;    }    // 其他异常处理方法}

当然这边还是有不规范的地方,没有用统一响应体进行返回,后面会介绍如何封装统一响应体。

九、分组校验

@Valid@Validated 两个注解都可以实现校验,前面的功能用 @Valid 也是可以的,但是 @Validated 功能更强大,可以实现分组校验。什么是分组校验,分组校验实际上实现了实体类的复用,有时候并不希望对所有的参数都进行校验,例如下面这个情况:

@Datapublic class Route {        @NotNull(message = "始发地省id不能为空")        private Integer startProvinceId;                @NotNull(message = "目的地省id不能为空")        private Integer endProvinceId;                @NotBlank(message = "详细地址不能为空")        private String address;}

假如在一个接口中只希望校验 startProvinceIdaddress ,而在另一个接口中只希望校验 endProvinceIdaddress ,这个时候就可以用分组校验。可以定义一个接口:

public interface ValidateGroup {        interface RouteValidStart {}        interface RouteValidEnd {}}

然后在实体类中添加分组:

@Datapublic class Route {        @NotNull(groups = {RouteValidStart.class}, message = "始发地省id不能为空")        private Integer startProvinceId;                @NotNull(groups = {RouteValidEnd.class}, message = "目的地省id不能为空")        private Integer endProvinceId;                @NotBlank(groups = {RouteValidStart.class, RouteValidEnd.class}, message = "详细地址不能为空")        private String address;}

然后在校验的时候只需要把分组传入 @Validate 就可以实现指定参数的校验:

@RequestMapping("addRoute")public ServerResponse addRoute(@RequestBody @Validated({RouteValidStart.class}) Route route) {        // ...        return ServerResponse.success();}

十、单个参数校验

在参数前面加上注解即可:

@PostMapping("/get")public ReturnVO getUserInfo(@RequestParam("userId") @NotNull(message = "用户ID不能为空") String userId){    return new ReturnVO().success();}

然后在 Controller 类上面增加 @Validated 注解,注意不是增加在参数前面。

读到这里,这篇"SpringBoot参数如何校验"文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注行业资讯频道。

参数 注释 元素 注解 处理 方法 接口 分组 前端 字符 字符串 数字 测试 全局 实体 数据 范围 长度 代码 地址 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 标准数据库服务器 最小数据库学习 恒生电子软件开发电脑 软件开发产品维保期是几年 西瓜美剧软件开发 樱桃视频软件开发 西安学习软件开发收费多少 服务器的商业模式 金蝶云星空支持哪两种数据库 软件开发进度与质量保证措施 腾讯贵州服务器云空间 用户信息网络安全管理制度 系统与数据库文件不一致性 苏州新焦点网络技术有限公司 网络安全与大数据分析专业 杨浦区特定网络技术转让操作 网络安全知识校园调查问卷 软件开发合同缺陷责任期 db2数据库表解锁 建党百年网络安全如何宣传 计算机的网络技术有什么好处 软件开发职业修炼三层境界 中创应用服务器软件v10价格 网络安全关键技术的主要内容 软件开发速度越来越慢 广州提莫互联网科技有限公司 松江区质量网络技术服务价目表 晨橙网络技术有限公司 软件开发合同赔偿金 用友数据库怎么删除科目
0