Skip to the content.

深入学习 Spring Web 开发 —— Validation

通过 Validation 注解,可以方便地定义需要以何种方式来校验接口参数。

快速开始

1. 引入 Validation 依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

2. 使用校验注解

@Data 为 lombok 的注解,在此不做详细说明。

使用校验注解,以定义需要以何种方式来校验接口参数。

@Data
public class UserRequest {

    @NotBlank
    private String name;

    @Min(0)
    @Max(200)
    private Integer age;

}

3. 声明校验参数

在 Controller 中,声明接口方法中需要校验的参数。

@RestController
public class UserController {

    @PostMapping("/api/users")
    public UserRequest createUser(@RequestBody @Validated UserRequest userRequest) {
        return userRequest;
    }

}

4. 捕捉全局异常

这一步不是必须的,目的是为了能方便查看校验结果。

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, Object>> handleMethodArgumentNotValidException(
            MethodArgumentNotValidException exception) {
        List<String> errorMessages = exception.getFieldErrors()
                .stream()
                .map(fieldError -> String.format("%s %s", fieldError.getField(), 
                        fieldError.getDefaultMessage()))
                .sorted()
                .collect(Collectors.toList());

        Map<String, Object> errorMessageMap = CollectionUtils.newLinkedHashMap(2);
        errorMessageMap.put("code", HttpStatus.BAD_REQUEST.value());
        errorMessageMap.put("message", errorMessages);

        return ResponseEntity.badRequest().body(errorMessageMap);
    }

}

5. 查看校验效果

使用 curl 访问接口:

curl --location --request POST 'http://localhost:8080/api/users' \
--header 'Content-Type: application/json' \
--data-raw '{
    "name": "",
    "age": -1
}'

最终的校验结果如下:

{
    "code": 400,
    "message": [
        "age 最小不能小于0",
        "name 不能为空"
    ]
}

常用注解

下面列举了,在日常的开发当中,我们经常需要使用到的 Validation 注解。这些注解,主要定义在 javax.validation 包下。

@NotNull、@NotEmpty 与 @NotBlank

注解 作用对象 含义
@NotNull 可以作用于任意对象 对象不能为 null
@NotEmpty 只能作用于 String、Collection、Map 和数组 对象不能为 null ,且对象的长度不能为 0
@NotBlank 只能作用于 String 字符串不能为 null ,且字符串的长度不能为 0

示例代码:

@Data
public class NotAnnotationRequest {

    @NotNull
    private Object notNullObject;

    @NotEmpty
    private String notEmptyString;

    @NotEmpty
    private Collection<Integer> notEmptyCollection;

    @NotEmpty
    private Map<String, String> notEmptyMap;

    @NotEmpty
    private int[] notEmptyArray;

    @NotBlank
    private String notBlankString;

}

@Size 与 @Min、@Max

注解 作用对象 含义
@Size 可以作用于 String、Collection、Map 和数组 包含两个变量:min 和 max,其中 min 的默认值为 0 ,表示对象的最小长度;max 的默认值为 Integer.MAX_VALUE ,表示对象的最大长度
@Min 可以作用于 BigDecimal、BigInteger,byte、short、int、long 以及它们的包装类型 表示数字的最小值
@Max 可以作用于 BigDecimal、BigInteger,byte、short、int、long 以及它们的包装类型 表示数字的最大值

示例代码:

@Data
public class RangeAnnotationRequest {

    @Size(min = 1, max = 5)
    private String limitedString;

    @Size(min = 1, max = 5)
    private Collection<Integer> limitedCollection;

    @Size(min = 1, max = 5)
    private Map<String, String> limitedMap;

    @Size(min = 1, max = 5)
    private int[] limitedArray;

    @Min(1)
    @Max(2)
    private Integer limitedNumber;

}

@Pattern

参数 含义
regexp 正则表达式
flags 标识正则表达式的模式,包含:
Pattern.Flag.UNIX_LINES、
Pattern.Flag.CASE_INSENSITIVE、
Pattern.Flag.COMMENTS、
Pattern.Flag.MULTILINE、
Pattern.Flag.DOTALL、
Pattern.Flag.UNICODE_CASE、
Pattern.Flag.CANON_EQ
共7种模式
message 错误提示信息(message 不是 @Pattern 特有的,所有 Validation 注解都包含该参数)

示例代码:

@Data
public class PatternAnnotationRequest {

    @Pattern(regexp = "^1[3456789]\\d{9}$", message = "手机号码格式不正确")
    private String mobile;

}

@Valid 与 @Validated

@Valid 和 @Validated 都可以用来声明校验参数,但它们并不完全一样,它们的区别主要包含以下几个方面:

使用 @Valid 声明校验参数

除了可以使用 @Validated 声明校验参数,也可以使用 @Valid 对需要校验的参数进行声明。

@RestController
public class UserController {

    @PostMapping("/api/users")
    public UserRequest createUser(@RequestBody @Valid UserRequest userRequest) {
        return userRequest;
    }

}

嵌套校验

可以使用 @Validated + @Valid,或 @Valid + @Valid 的方式共同定义嵌套校验。

使用 @Valid 指定需要校验的接口参数;使用 @Validated 或 @Valid 声明 Controller 的接口方法中,需要校验的参数。

使用校验注解,指定需要校验的接口参数:

@Data
public class CompanyRequest {

    @NotBlank
    private String name;

    @Valid
    @NotNull
    private AddressRequest address;

}
@Data
public class AddressRequest {

    @NotBlank
    private String country;

    @NotBlank
    private String province;

    @NotBlank
    private String city;

}

声明 Controller 的接口方法中,需要校验的参数:

@RestController
public class NestValidController {

    @PostMapping("/api/companies")
    public CompanyRequest createCompany(@RequestBody @Validated CompanyRequest companyRequest) {
        return companyRequest;
    }

}

分组校验

分组校验可以使用在参数定义相同、接口校验不同的场景中。

定义分组:

public interface CreateGroup extends Default {
}
public interface UpdateGroup extends Default {
}

使用校验注解,指定需要校验的接口参数:

@Data
public class OrganizationRequest {

    @NotBlank(groups = CreateGroup.class)
    @Null(groups = UpdateGroup.class)
    private String orgCode;

    @NotBlank
    private String orgName;

}

声明 Controller 的接口方法中,需要校验的参数:

@RestController
public class GroupValidController {

    @PostMapping("/api/organizations")
    public OrganizationRequest createOrganization(
            @RequestBody @Validated(CreateGroup.class) OrganizationRequest organizationRequest) {
        return organizationRequest;
    }

    @PutMapping("/api/organizations")
    public OrganizationRequest updateOrganization(
            @RequestBody @Validated(UpdateGroup.class) OrganizationRequest organizationRequest) {
        return organizationRequest;
    }

}

其他注解

除了常用的注解,javax.validation 下还定义了下面这些注解,以供我们在有需要时选择使用。

@Null

注解 作用对象 含义
@Null 可以作用于任意对象 对象必须为 null

示例代码:

@Data
public class NullAnnotationRequest {

    @Null
    private Object nullObject;

}

@Email

注解 作用对象 含义
@Email 只能作用于 String 字符串必须为合法的 email

示例代码:

@Data
public class EmailAnnotationRequest {

    @Email
    private String email;

}

布尔类型注解

注解 作用对象 含义
@AssertTrue 可以作用于 boolean 和 Boolean 对象必须为 true
@AssertFalse 可以作用于 boolean 和 Boolean 对象必须为 false

示例代码:

@Data
public class BooleanAnnotationRequest {

    @AssertTrue
    private Boolean trueParam;

    @AssertFalse
    private boolean falseParam;

}

数字类型注解

注解 作用对象 含义
@DecimalMin 可以作用于数字类型和字符串 表示数字的最小值
@DecimalMax 可以作用于数字类型和字符串 表示数字的最大值
@Digits 可以作用于数字类型和字符串 包含两个变量:integer 和 fraction,其中 integer 表示整数位的最大位数;fraction 表示小数位的最大位数
@Negative 可以作用于数字类型 表示必须为负数
@NegativeOrZero 可以作用于数字类型 表示必须为负数或零
@Positive 可以作用于数字类型 表示必须为正数
@PositiveOrZero 可以作用于数字类型 表示必须为正数或零

示例代码:

@Data
public class NumberAnnotationRequest {

    @DecimalMin("1.5")
    @DecimalMax("2.5")
    private BigDecimal limitedBigDecimal;

    @Digits(integer = 1, fraction = 2)
    private BigDecimal digits;

    @Negative
    private short negative;

    @NegativeOrZero
    private int negativeOrZero;

    @Positive
    private long positive;

    @PositiveOrZero
    private BigDecimal positiveOrZero;

}

时间类型注解

注解 作用对象 含义
@Past 可以作用于时间类型 表示必须为过去的时间
@PastOrPresent 可以作用于时间类型 表示必须为过去的时间或现在
@Future 可以作用于时间类型 表示必须为将来的时间
@FutureOrPresent 可以作用于时间类型 表示必须为将来的时间或现在

示例代码:

@Data
public class TimeAnnotationRequest {

    @Past
    private Date past;

    @PastOrPresent
    private Date pastOrPresent;

    @Future
    private Date future;

    @FutureOrPresent
    private Date futureOrPresent;

}

返回首页 获取源码