如何在Spring Boot中优雅的进行参数校验?

1. 前言

在平时的开发工作中,我们通常需要对接口进行参数格式验证。当参数个数较少(个数小于3)时,可以使用if ... else ...手动进行参数验证。当参数个数大于3个时,使用if ... else ...进行参数验证就会让代码显得臃肿,这个时候推荐使用注解来进行参数验证。

在Java中,注解(Annotation)是一种代码标记,通常用于提供元数据,这些元数据可以被编译器或运行时环境使用。这些注解通常用于框架和库中,以实现更加灵活和可配置的代码。

2. 常用注解描述

  1. @NotNull

    • 描述:标记一个值不能为null。
    • 示例:
    public class User {  @NotNull  private String name;  // ...  
    }
    
  2. @NotEmpty

    • 描述:标记一个集合(如List、Set等)不能为空。
    • 示例:
    public class User {  @NotEmpty  private List<String> interests;  // ...  
    }
    
  3. @NotBlank

    • 描述:标记一个字符串不能为空白(即null、空字符串或只包含空格)。
    • 示例:
    public class User {  @NotBlank  private String username;  // ...  
    }
    
  4. @Size

    • 描述:标记一个字符串或集合的大小必须在指定的范围内。
    • 示例:
    public class User {  @Size(min = 2, max = 50)  private String username;  // ...  
    }
    
  5. @Min@Max

    • 描述:标记一个数值必须在指定的最小值和最大值之间。
    • 示例:
    public class User {  @Min(18)@Max(60)  private int age;  // ...  
    }
    
  6. @DecimalMin@DecimalMax

    • 描述:标记一个浮点数或双精度数必须在指定的最小值和最大值之间。
    • 示例:
    public class User {  @DecimalMin("0.01") @DecimalMax("100.00")  private double discount;  // ...  
    }
    
  7. @Digits

    • 描述:标记一个整数或浮点数必须在指定的精度和总数值范围内。
    • 示例:
    public class User {  @Digits(integer = 3, fraction = 2) // 总长度为5,3位整数,2位小数。例如:"123.45" 是合法的。  private BigDecimal amount;  // ...  
    }
    
  8. @Pattern

    • 描述:标记一个字符串必须匹配指定的正则表达式。通常用于验证输入格式。例如电子邮件地址、电话号码格式等。@Pattern注解在javax.validation.constraints包中。@Pattern(regexp = “^\w{5,}$”)表示长度在5-20之间,由字母、数字、下划线组成的字符串。@Pattern注解用于类字段上,例如用户密码字段。
    • 示例:
    public class User { @Pattern(regexp = "^[a-zA-Z0-9]*$") private String password; //... 
    } 
    
  9. @Email

    • 描述:标记一个字符串必须是一个有效的电子邮件地址。
    • 示例:
    @Email
    private String emailAddress;
    
  10. @AssertTrue@AssertFalse

    • 描述:标记一个布尔值必须为true或false。
    • 示例:
    @AssertTrue
    private boolean isValid;@AssertFalse
    private boolean isNotValid;
    
  11. @Future

    • 描述:标记一个日期必须是在未来某个时间点之后。
    • 示例:
    @Future
    private Date expiryDate;
    
  12. @Past

    • 描述:标记一个日期必须是在过去某个时间点之前。
    • 示例:
    @Past
    private Date purchaseDate;
    

这些注解通常与验证框架(如Hibernate Validator)一起使用,以在运行时验证对象的属性。

3. 注解使用

  1. 在项目的pom.xml文件中添加如下依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>
  1. 在实体类中使用上述注解,代码如下:

    package com.yyqq.demo.entity;import lombok.Data;
    import javax.validation.constraints.*;@Data
    public class User {@NotBlank(message = "用户姓名不能为空")private String name;@NotBlank(message = "密码不能为空")@Size(min = 6, message = "密码长度不能少于6位")private String password;@Min(value = 0, message = "年龄不能小于0岁")@Max(value = 150, message = "年龄不应超过150岁")private Integer age;@Pattern(regexp = "^((13[0-9])|(15[^4])|(18[0-9])|(17[0-9])|(147))\d{8}$", message = "手机号格式不正确")private String phone;
    }
    
  2. 控制器类使用验证,代码如下:

    import com.yyqq.demo.util.Result;
    import com.yyqq.demo.entity.User;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;import javax.validation.Valid;@RestController
    @RequestMapping("/user")
    public class UserController {@PostMapping("/add")public Result add(@Valid @RequestBody User user) {return Result.success(user);}
    }
    
  3. Result是封装结果的一个类,用于返回统一的结果,代码如下:

    package com.yyqq.demo.util;import lombok.Data;
    import java.io.Serializable;@Data
    public class Result<T> implements Serializable {private int code;private boolean success;private T data;private String msg;private Result(int code, T data, String msg) {this.code = code;this.data = data;this.msg = msg;this.success = code == 200;}public static <T> Result<T> sucess(T data) {return new Result<>(200, data, null);}public static <T> Result<T> fail(String msg) {return new Result<>(500, null, msg);}
    }
    
  4. 定义全局异常处理类,我们在全局异常处理类中使用ExceptionHandler捕获BindException异常,获取参数验证异常信息,最后返回统一的异常结果格式,代码如下:

    package com.yyqq.demo.util;import com.yyqq.demo.util.Result;
    import org.springframework.validation.BindException;
    import org.springframework.validation.BindingResult;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.RestControllerAdvice;@RestControllerAdvice
    public class GlobalExceptionHandler {@ExceptionHandler(BindException.class)public Result handleError(BindException e) {BindingResult bindingResult = e.getBindingResult();return Result.fail(bindingResult.getFieldError().getDefaultMessage());}
    }
    

4. 使用分组验证

  1. 用于插入记录时的分组验证,代码如下:
package com.yyqq.demo.interceptor;
import javax.validation.groups.Default;public interface Insert extends Default {
}
  1. 用于更新记录时的分组验证,代码如下:
package com.yyqq.demo.interceptor;
import javax.validation.groups.Default;public interface Update extends Default {
}
  1. 在实体类中进行分组标记,代码如下:
package com.yyqq.demo.entity;import lombok.Data;
import javax.validation.constraints.*;@Data
public class User {@NotBlank(groups = {Insert.class, Update.class})@NotBlank(message = "用户姓名不能为空")private String name;@NotBlank(message = "密码不能为空")@Size(min = 6, message = "密码长度不能少于6位")private String password;@Min(value = 0, message = "年龄不能小于0岁")@Max(value = 150, message = "年龄不应超过150岁")private Integer age;@NotBlank(groups = {Insert.class, Update.class})@Pattern(regexp = "^((13[0-9])|(15[^4])|(18[0-9])|(17[0-9])|(147))\\d{8}$", message = "手机号格式不正确")private String phone;
}
  1. 控制器类使用分组验证
package com.yyqq.demo.controller;import com.yyqq.demo.util.Result;
import com.yyqq.demo.entity.User;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.validation.Valid;@RestController
@RequestMapping("/user")
public class UserController {@PostMapping("/add")public Result add(@Validated(Insert.class) @RequestBody User user) {return Result.success(user);}@PostMapping("/update")public Result update(@Validated(Update.class) @RequestBody User user) {return Result.success(user);}
}

5. 自定义验证注解

除了框架自带的注解,平时的工作中可能需要我们自定义验证注解处理特定的业务需求。

  1. 定义注解
package com.yyqq.demo.validate;import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;@Documented
@Retention(RUNTIME)
@Constraint(validatedBy = {PhoneValidator.class})
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
public @interface Phone {String message() default "手机号格式错误";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}
  1. 定义验证器类
package com.yyqq.demo.validate;import javax.validation.ConstraintValidatorContext;
import javax.validation.ConstraintValidator;
import java.util.regex.Pattern;public class PhoneValidator implements ConstraintValidator<Phone, String> {private static final String REGEX = "^((13[0-9])|(15[^4])|(18[0-9])|(17[0-9])|(147))\\d{8}$";@Overridepublic boolean isValid(String s, ConstraintValidatorContext context) {boolean result = false;try {result = Pattern.matches(REGEX, s);} catch (Exception e) {System.out.println("验证手机号格式时发生异常,异常信息:" + e);}return result;}
}
  1. 实体类使用注解
package com.yyqq.demo.validate;public class User {//其他属性...//  @Pattern(regexp = "^((13[0-9])|(15[^4])|(18[0-9])|(17[0-9])|(147))\\d{8}$", message = "手机号格式不正确")@Phoneprivate String phone;
}

6. @Valid与@Validated的区别

用于参数校验的注解通常有两个:@Valid@Validated。它们的区别有如下几点:

区别@Valid@Validated
来源@Valid是Java标准注解@Validated是Spring框架定义的注解。
是否支持分组验证不支持支持
使用位置构造函数、方法、方法参数、成员属性类、方法、方法参数,不能用于成员属性
是否支持嵌套校验支持不支持

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

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

相关文章

【MVT_1703230471】基于Python NLTK分词、词云、LDA主题分类及GPT情感分类

【Talk is cheap】 1 数据探索 case idcase outcome case title case text 0 Case1 cited Alpine Hardwood (Aust) Pty Ltd v Hardys Pty Lt... Ordinarily that discretion will be exercised s... 1 Case2 cited Black v Lipovac [1998] FCA 699 ; (1998) 217 AL... The gen…

【WPF.NET开发】创建模板

本文内容 何时创建 ControlTemplate先决条件创建 ControlTemplate使用模板添加触发器使用 VisualState 使用 Windows Presentation Foundation (WPF)&#xff0c;可以使用自己的可重用模板自定义现有控件的可视结构和行为。 可以对应用程序、窗口和页面全局应用模板&#xff…

LeetCode-146.LRU缓存(Python)

此题看题解 题目链接 class ListNode:def __init__(self, keyNone, valueNone):self.key keyself.value valueself.prev Noneself.next Noneclass LRUCache:def __init__(self, capacity: int):self.capacity capacityself.cache {}self.head ListNode()self.tail Li…

[Android]CheckBox复选框

在Android开发中&#xff0c;复选框&#xff08;CheckBox&#xff09;是一种常用的控件&#xff0c;用于让用户在多个选项中进行选择。它通常用于表单中&#xff0c;让用户选择多个选项或者进行多项操作。在本篇博客中&#xff0c;我们将介绍如何在Android应用中使用CheckBox控…

Unity内置的四种渲染管线简介

Unity的渲染管线&#xff08;Rendering Pipeline&#xff09;负责将游戏场景转化为可以在屏幕上显示的像素。 Unity 提供以下渲染管线&#xff08;官方文档的介绍&#xff09;&#xff1a; 内置渲染管线是 Unity 的默认渲染管线。这是通用的渲染管线&#xff0c;其自定义选项…

《Python》面试常问:深拷贝、浅拷贝、赋值之间的关系(附可变与不可变)【用图文讲清楚!】

背景 想必大家面试或者平时学习经常遇到问python的深拷贝、浅拷贝和赋值之间的区别了吧&#xff1f;看网上的文章很多写的比较抽象&#xff0c;小白接收的难度有点大&#xff0c;于是乎也想自己整个文章出来供参考 可变与不可变 讲深拷贝和浅拷贝之前想讲讲什么是可变数据类型…

Win10 使用 Nmap 扫描 Andorid 设备开放端口

Nmap Nmap 是 网络探测工具和安全/端口扫描器。 官网链接 Nmap参考指南(Man Page) 官网下载地址 Downloading Nmap Nmap 下载安装 到官网下载对应操作系统的安装包&#xff0c; 默认配置&#xff0c;一直下一步安装即可。安装过程中备份下安装路径&#xff0c;后续用到。…

九州未来大模型一体机,加速AI应用落地

2023年已接近尾声&#xff0c;对于中国人工智能产业而言&#xff0c;这一年注定成为一个重要的转折点。在不同行业中&#xff0c;人工智能的应用不断涌现&#xff0c;众多大模型如雨后春笋般涌现。然而&#xff0c;在这一发展过程中也不可避免地出现了一系列问题&#xff0c;包…

Java项目启动过程中Mybatis报错bug(多问题汇总,持续更新)

目录 前言1. Error: Method queryTotal execution error of sql2. Cause: java.sql.SQLSyntaxErrorException: Unknown column xxxxx in fiel其他前言 统计实战中关于Mybatis出错的原因 1. Error: Method queryTotal execution error of sql 执行Springboot的时候出现如下错…

vcruntime140_1.dll文件下载安装方法分享,如何安全修复vcruntime140_1.dll

遇到vcruntime140_1.dll文件遗失是用户在使用Windows操作系统时较为普遍遭遇到的一个问题&#xff0c;它归类于动态链接库&#xff08;DLL&#xff09;文件缺失的常见情形。在今天的内容里&#xff0c;我们将深入探讨如何正确地下载和安装vcruntime140_1.dll文件&#xff0c;以…

dangerouslyUseHTMLString选项来启用自定义 HTML 片段

Old&#xff1a; const msg 切换旧版成功&#xff0c;推荐 <span id"changeMsg" style"color: #47FFF1;; text-decoration: underline;">切换新版</span> 体验最新效果;const message ElMessage({customClass: site-toast,dangerouslyUseHTML…

得到山形数组的最少删除次数(LeetCode日记)

LeetCode-1671-得到山形数组的最少删除次数 题目信息: 我们定义 a r r arr arr 是 山形数组 当且仅当它满足&#xff1a; a r r . l e n g t h > 3 arr.length > 3 arr.length>3存在某个下标 i i i &#xff08;从 0 开始&#xff09; 满足 0 < i < a r r…

Google推出Gemini AI开发——10年工作经验的Android开发要被2年工作经验的淘汰了?

应用程序中利用 Gemini 前言&#xff08;可略过&#xff09;、使用 Gemini Pro 开发应用程序正文、Android Studio 中构建Gemini API Starter 应用第 1 步&#xff1a;在 AI 的新项目模板的基础上进行构建第 2 步&#xff1a;生成 API 密钥第 3 步&#xff1a;开始原型设计 正文…

Mybatis之增删改查

目录 一、引言 二、Mybatis——增 举例&#xff1a;添加用户 三、Mybatis——删 举例&#xff1a;删除用户 四、Mybatis——改 举例&#xff1a;修改用户 五、Mybatis——查 六、注意 END&#xff1a; 一、引言 书接上回&#xff0c;我们在了解完mybatis之后&#xff0c;肯…

STM32F4 HAL流水灯Proteus仿真

源码下载&#xff1a;https://download.csdn.net/download/zlkk00/88654405

CSS基础小练习

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>圣诞节快乐</title><style>/*设置背景色*/body{background-image:linear-gradient(green 50%,red 50%);background-size:100% 30px;}/*让div在页面居中*/#text{…

ubuntu 18/20/22 安装 mysql 数据库

这里写自定义目录标题 ubuntu 18/20/22 安装 mysql 数据库1. 准备2. 安装 mysql3. 配置4. 测试 demo 用户5 服务管理5.1 查看服务状态5.2 启动服务5.3 停止服务5.4 重启服务 ubuntu 18/20/22 安装 mysql 数据库 1. 准备 安装前需要知道 root 用户的密码 假如不知道 root 用户…

20231223使用Rockchip原厂的Android11调通Firefly的AIO-3399J开发板上的AP6356S

20231223使用Rockchip原厂的Android11调通Firefly的AIO-3399J开发板上的AP6356S 2023/12/23 14:14 开发板&#xff1a;Firefly的AIO-3399J【RK3399】 SDK&#xff1a;rk3399-android-11-r20211216.tar.xz【Android11】 Android11.0.tar.bz2.aa【ToyBrick】 Android11.0.tar.bz2…

C# Onnx yolov8n csgo player detection

目录 效果 模型信息 项目 代码 下载 C# Onnx yolov8n csgo player detection 效果 模型信息 Model Properties ------------------------- date&#xff1a;2023-12-22T15:01:08.014205 author&#xff1a;Ultralytics task&#xff1a;detect license&#xff1a;AGPL-…

学会这套Pytest接口自动化测试框架,击败99%的人

Pytest 的下载安装 1、Python3 使用 pip install -U pytest 安装 2、查看 pytest 版本信息 pytest --version 3、pytest 用例的执行规则&#xff1a; ①测试文件以 test_xx.py 命名需要以 test_开头&#xff08;或_test 结尾&#xff09; ②测试类以 Test_开头&#xff0…