补充:使用自定义参数注解

一、为什么使用 Validation 来验证参数

通常我们在使用spring框架编写接口时,对于部分接口的参数我们要进行判空或者格式校验来避免程序出现异常。那是我
们一般都是使用if-else逐个对参数进行校验。这种方法按逻辑来说也是没有问题的,同样也能实现预期效果。但是,这样的代码从可读性以及美观程序来看,是非常糟糕的。那么,我们就可以使用@valid注解来帮助我们优雅的校验参数。

二、如何使用Validation相关注解进行参数校验

①为实体类中的参数或者对象添加相应的注解;②在控制器层进行注解声明,或者手动调用校验方法进行校验;③对异常进行处理;

三、Validation类的相关注解及描述

嵌套注解 @Valid(javax.validation.Valid)

空和非空检查: @Null、@NotNull、@NotBlank、@NotEmpty

注解 支持Java类型 备注
@Null Object 验证元素值为null
@NotNull Object 验证元素值不能为 null
@NotBlank CharSequence 验证元素值不为null且移除两边空格后长度大于0
@NotEmpty CharSequence,Collection,Map and Arrays 验证元素值不为null且不为空(字符串长度不为0、集合大小不为0)

Boolean值检查: @AssertTrue、@AssertFalse

注解 支持Java类型 备注
@AssertTrue Boolean, boolean 验证元素值必须为true,否则抛异常
@AssertFalse Boolean, boolean 验证元素值必须为flase

长度检查: @Size、@Length

注解 支持Java类型 备注
@Size String,Collection,Map,arrays,CharSequence 验证元素个数包含在一个区间
@Length CharSequence 验证元素值包含在一个区间

日期检查: @Future、@FutureOrPresent、@Past、@PastOrPresent

注解 支持Java类型 备注
@Future java.util.Date, java.util.Calendar 验证日期为当前时间之后
@FutureOrPresent java.util.Date, java.util.Calendar 验证日期为当前时间或之后一个时间
@Past java.util.Date, java.util.Calendar 验证日期为当前时间之前
@PastOrPresent java.util.Date, java.util.Calendar 验证日期为当前时间或之前

其它检查: @Email、@CreditCardNumber、@URL、@Pattern、@ScriptAssert、@UniqueElements

注解 支持Java类型 备注
@Email CharSequence 验证日期为当前时间之后
@CreditCardNumber CharSequence 验证日期为当前时间或之后一个时间
@URL CharSequence 验证日期为当前时间之前
@Pattern CharSequence 验证日期为当前时间或之前
@Valid Object 验证关联对象元素进行递归校验检查
@UniqueElements Collection 校验集合中的元素必须保持唯一 否则异常

数值检查: @Min、@Max、@Range、@DecimalMin、@DecimalMax、@Digits

注解 支持Java类型 备注
@Min BigDecimal, BigInteger, byte, short,int, long,Number. 检验当前数值大于等于指定值
@Max CharSequence 检验当前数值小于等于指定值
@Range CharSequence 验证数值为指定值区间范围内
@DecimalMin CharSequence 验证数值是否大于等于指定值
@DecimalMax Object 验证数值是否小于等于指定值
@Digits(integer = 3, fraction = 2) 验证注解的元素值的整数位数和小数位数上限

四、使用 Validation API 进行参数效验步骤

整个过程如下图所示,用户访问接口,然后进行参数效验。 对于GET请求的参数可以使用@validated注解配合上面相应的注解进行校验或者按照原先if-else方式进行效验。而对于POST请求,大部分是以表单数据即以实体对象为参数,可以使用@Valid注解方式进行效验。如果效验通过,则进入业务逻辑,否则抛出异常,交由全局异常处理器进行处理。
在这里插入图片描述
第一种:在Controller方法参数前加@Valid注解——校验不通过时直接抛异常,get请求直接在平面参数前添加相应的校验规则注解,使用这种的话一般结合统一异常处理进行处理;

第二种:在Controller方法参数前加@Valid注解,参数后面定义一个BindingResult类型参数——执行时会将校验结果放进bindingResult里面,用户自行判断并处理。

* 将校验结果放进BindingResult里面,用户自行判断并处理 * @param userInfo * @param bindingResult * @return @PostMapping ( "/testBindingResult" ) public String testBindingResult ( @RequestBody @Valid UserInfo userInfo , BindingResult bindingResult ) { // 参数校验 if ( bindingResult . hasErrors ( ) ) { String messages = bindingResult . getAllErrors ( ) . stream ( ) . map ( ObjectError : : getDefaultMessage ) . reduce ( ( m1 , m2 ) - > m1 + ";" + m2 ) . orElse ( "参数输入有误!" ) ; //这里可以抛出自定义异常,或者进行其他操作 throw new IllegalArgumentException ( messages ) ; return "操作成功!" ;

这里我们是直接抛出了异常,如果没有进行全局异常处理的话,接口将会返回如下信息:
在这里插入图片描述
第三种:用户手动调用对应API执行校验——Validation.buildDefault ValidatorFactory().getValidator().validate(xxx)

这种方法适用于校验任意一个有valid注解的实体类,并不仅仅是只能校验接口中的参数;

这里我提取出一个工具类,如下:

MyValidationUtils.class

import org.springframework.util.CollectionUtils;
import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import javax.validation.Validation;
import java.util.Set;
 * 手动调用api方法校验对象
public class MyValidationUtils {
    public static void validate(@Valid Object user) {
        Set<ConstraintViolation<@Valid Object>> validateSet = Validation.buildDefaultValidatorFactory()
                .getValidator()
                .validate(user, new Class[0]);
        if (!CollectionUtils.isEmpty(validateSet)) {
            String messages = validateSet.stream()
                    .map(ConstraintViolation::getMessage)
                    .reduce((m1, m2) -> m1 + ";" + m2)
                    .orElse("参数输入有误!");
            throw new IllegalArgumentException(messages);

五、springboot项目中实战演练

Spring Boot 2.3后,starter-web不再包含子依赖starter-validation,需要手动导入。

1.对实体类的变量进行注解标注

实体类中添加 @Valid 相关验证注解,并在注解中添加出错时的响应消息。

User.class

@Data
public class User {
    @NotBlank(message = "姓名不能为空")
    private String username;
    @NotBlank(message = "密码不能为空")
    @Length(min = 6, max = 16, message = "密码长度为6-16位")
    private String password;
    @Pattern(regexp = "0?(13|14|15|17|18|19)[0-9]{9}", message = "手机号格式不正确")
    private String phone;
    // 嵌套必须加 @Valid,否则嵌套中的验证不生效
    @Valid
//message为验证后的返回消息
    @NotNull(message = "userinfo不能为空")
    private UserInfo userInfo;

2.创建自定义异常

自定义异常类,方便我们处理手动抛出的异常。

public class ParamaErrorException extends RuntimeException {

public ParamaErrorException() {
public ParamaErrorException(String message) {
    super(message);

3.自定义响应枚举类

定义一个返回信息的枚举类,方便我们快速响应信息,不必每次都写返回消息和响应码。

public enum ResultEnum {
    SUCCESS(1000, "请求成功"),
    PARAMETER_ERROR(1001, "请求参数有误!"),
    UNKNOWN_ERROR(9999, "未知的错误!");
    private Integer code;
    private String message;
    ResultEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    public Integer getCode() {
        return code;
    public String getMessage() {
        return message;

4.自定义响应对象类

创建用于返回调用方的响应信息的实体类。

@Data
public class ResponseResult {
    private Integer code;
    private String msg;
    public ResponseResult() {
    public ResponseResult(ResultEnum resultEnum) {
        this.code = resultEnum.getCode();
        this.msg = resultEnum.getMessage();
    public ResponseResult(Integer code, String msg) {
        this.code = code;
        this.msg = msg;

5.接口类中添加相关注解

处理get请求直接在参数前添加验证注解,处理post请求时在对象前添加@Valid注解

TestController.class

@Validated @RestController @Api(value = "测试使用validation验证参数") public class TestController { * 测试get方法,手动if进行判空,校验失败时手动抛出自定义异常 * @param username 姓名 * @return ResponseResult @ApiOperation(value = "测试get方法", notes = "输入用户名") @GetMapping("/testGet") public ResponseResult testGet(String username) { if (username == null || "".equals(username)) { throw new ParamaErrorException("username 不能为空"); return new ResponseResult(ResultEnum.SUCCESS); * 使用注解校验get请求平面参数,需要在Controller类头部添加@Validated注解,否则不能成功校验,这种方法不用手动抛出异常 * @param username * @return @ApiOperation(value = "测试get方法", notes = "输入用户名") @GetMapping("/testGetByValidated") public ResponseResult testGetByValidated(@Length(max = 4) @RequestParam("username") String username) { return new ResponseResult(ResultEnum.SUCCESS); @ApiOperation(value = "post方法传入单个对象", notes = "传入json对象") @PostMapping("/testUserInfo") public ResponseResult testUserInfo(@Valid @RequestBody UserInfo userInfo) { return new ResponseResult(ResultEnum.SUCCESS); * post方法传入对象,手动校验,此时参数前没有添加@Valid注解,所以不会自动进行校验,手动调用validate方法进行校验,失败时会抛出异常 * @param userInfo * @return ResponseResult @ApiOperation(value = "post方法传入对象,手动测试", notes = "单个对象") @PostMapping("/checkByMethod") public ResponseResult checkByMethod(@RequestBody UserInfo userInfo) { //调用api校验 MyValidationUtils.validate(userInfo); return new ResponseResult(ResultEnum.SUCCESS); * post方法传入多个对象,当使用@Valid校验对象集合时,要在控制层添加@Validated注解,否则不会对集合中的每个对象进行校验 * @param userInfo * @return ResponseResult @ApiOperation(value = "post方法传入多个对象", notes = "多个对象") @PostMapping("/testUserList") public ResponseResult testUserList(@Valid @RequestBody List<UserInfo> userInfo) { return new ResponseResult(ResultEnum.SUCCESS); * 测试对象中嵌套对象的情况,此时也要在对象属性上添加@Valid注解 * @param user * @return @ApiOperation(value = "测试对象中嵌套对象的情况") @PostMapping("/checkUser") public ResponseResult checkUser(@Valid @RequestBody User user) { return new ResponseResult(ResultEnum.SUCCESS); * 将校验结果放进BindingResult里面,用户自行判断并处理 * @param userInfo * @param bindingResult * @return @PostMapping("/testBindingResult") public String testBindingResult(@RequestBody @Valid UserInfo userInfo, BindingResult bindingResult) { // 参数校验 if (bindingResult.hasErrors()) { String messages = bindingResult.getAllErrors() .stream() .map(ObjectError::getDefaultMessage) .reduce((m1, m2) -> m1 + ";" + m2) .orElse("参数输入有误!"); //这里可以抛出自定义异常,或者进行其他操作 throw new IllegalArgumentException(messages); return "操作成功!";

补充:使用自定义参数注解

1.我们这里创建一个身份证校验注解

@Documented
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IdentityCardNumberValidator.class)
public @interface IdentityCardNumber {
    String message() default "身份证号码不合法";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};

这个注解是作用在Field字段上,运行时生效,触发的是IdentityCardNumber这个验证类。

message 定制化的提示信息,主要是从ValidationMessages.properties里提取,也可以依据实际情况进行定制
groups 这里主要进行将validator进行分类,不同的类group中会执行不同的validator操作
payload 主要是针对bean的,使用不多。
                    目录一、为什么使用 Validation 来验证参数二、如何使用Validation相关注解进行参数校验三、Validation类的相关注解及描述四、使用 Validation API 进行参数效验步骤五、 Spring Validation的三种校验方式五、springboot项目中实战演练补充:使用自定义参数注解一、为什么使用 Validation 来验证参数通常我们在使用spring框架编写接口时,对于部分接口的参数我们要进行判空或者格式校验来避免程序出现异常。那是我  们一般都是
				
文章目录1. 基础简介2. 框架简介2.1. validation-api2.2. jakarta.validation-api2.3. hibernate-validator2.4. spring-boot-starter-validation3. 注解说明3.1. 标识注解3.2. 约束注解3.3. 提示信息4. 使用说明4.1. 一般性校验4.2. 自定义校验4.3. 分组校验4.4. 异常处理 本文需要结合SpringBoot 封装接口返回数据的统一结构部分代码。 本文需要结合SpringBoot
<groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.1.Final</version> </dependency> 2.内部注释对象: @AssertFalse:用于boolean字段,该字段只能为t 要使用参数校验注解,需要引入以下依赖,注意springboot2.0的web模块已经包含此依赖 <dependency> <groupId>jakarta.validation</groupId> <artifactId>jakarta.validation-api</artifactId> </dependency> 添加了这个jar包,仅 <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.1.Final</version> </dependency> import java.io.Serializabl
作为服务端开发,验证前端传入的参数的合法性是一个必不可少的步骤,但是验证参数是一个基本上是一个体力活,而且冗余代码繁多,也影响代码的可阅读性,所以有没有一个比较优雅的方式来解决这个问题? 这么简单的问题当然早就有大神遇到并且解决了,这一篇文章主要讲一下解决基于spring-boot的验证参数的比较好的方法:利用validator-api来进行验证参数。 在spring-boot-sta
protected ValidationResult validateForm(Object form) { Set<ConstraintViolation<Object>> errors = validator.validate(form);
在后端开发时,不可避免的需要处理一些校验, 如果是写if-else这种代码去校验, 那会有一大段这样的代码。不过还好有个校验插件:javax.validation.validation-api,不过一般会引用hibernate的校验组件:org.hibernate.hibernate-validator, 它已经引用了validation-api组件。 首先pom.xml引入依赖 <!-- validator --> <dependency>
Spring Boot中,可以使用JSR-303 Bean Validation框架进行参数校验。在进行参数校验前,需要先定义一个实体类来封装需要校验参数。例如: ```java public class User { @NotBlank(message = "用户名不能为空") private String username; @NotBlank(message = "密码不能为空") private String password; @Email(message = "邮箱格式不正确") private String email; // getter and setter 在上面的代码中,使用了JSR-303 Bean Validation框架提供的注解来定义参数校验规则。例如@NotBlank用于校验字符串不能为空,@Email用于校验邮箱格式是否正确。 然后,在Controller中的方法参数使用@Valid注解来启用参数校验,例如: ```java @RestController public class UserController { @PostMapping("/users") public User createUser(@Valid @RequestBody User user) { // 处理创建用户的逻辑 在上面的代码中,使用@Valid注解来启用参数校验,并将需要校验参数作为方法参数传入。如果参数校验不通过,则会抛出MethodArgumentNotValidException异常,可以在ControllerAdvice中进行全局异常处理。