参数校验(二):参数校验的详细使用以及全局异常处理和自定义校验注解

Springboot参数校验(一)

前言

在项目中,前端传给后端的数据前端需要校验一遍,但是如果有些人绕过前端,直接向后端接口请求,如果后端没有进行参数校验,就会造成严重的问题。

如果要解决这个问题,我们可以采取的方式有很多种,在Controller层写if语句进行判断,但是这样不仅会造成代码的冗长和代码难以阅读和维护。

Springboot为我们提供了一个参数校验框架

Springboot集成Hibernate-Validator参数校验

Spring Boot Validation是Spring Boot整合了Hibernate Validation的一个框架,作用是检验客户端向服务器端提交的请求参数的基本格式是否合法。

引入依赖

Java API规范(JSR303)定义了Bean校验的标准validation-api,但没有提供实现。hibernate validator是对这个规范的实现,并增加了校验注解如@Email、@Length等。

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

一般来说:

  • POST、PUT请求,使用requestBody传递参数;
  • GET请求,使用requestParam/PathVariable传递参数。

常用注解

空和非空检查

名称场景
@Null被注释的元素必须为 null
@NotNull被注释的元素必须不为 null
@NotEmpty被注释的字符串的必须非空,集合对象的元素不为 0 ,即集合不为空一般用于list或map
@NotBlank只能用于字符串不为 null ,并且字符串 trim()以后 length 要大于 0 一般用于字符串

数值检查

名称场景
@Max(value)该字段的值只能小于或等于该值。值可以为int类型也可以为字符串类型
@Min(value)该字段的值只能大于或等于该值。值可以为int类型也可以为字符串类型
@DecimalMax(value)不常用-被注释的元素必须是一个数字,其值必须小于等于指定的最大值。如果参数的值是null,则表示有效
@DecimalMin(value)不常用-被注释的元素必须是一个数字,其值必须大于等于指定的最小值。如果参数的值是null,则表示有效
@Digits(integer, fraction)不常用-注释的元素必须是一个数字,其值必须在可接受的范围内。如果参数的值是null,则表示有效
@Positive不常用-判断正数。如果参数的值是null,则表示有效
@PositiveOrZero不常用-判断正数或 0 。如果参数的值是null,则表示有效
@Negative不常用-判断负数。如果参数的值是null,则表示有效
@NegativeOrZero不常用-判断负数或 0。如果参数的值是null,则表示有效

Boolean 值检查

名称场景
@AssertFalse被注释的元素必须为 true
@AssertTrue被注释的元素必须为 false

长度检查

名称场景
@Size(max, min)检查该字段的 size 是否在 min 和 max 之间,可以是字符串、数组、集合、Map 等。一般用集合
@Range用于校验数字或者字符串的长度是否在指定的区间之内。一般用于数字范围
@Length验证字符串的长度是否在指定的区间之内。用于字符串

日期检查

不常用

名称场景
@Future被注释的元素必须是一个将来的日期
@FutureOrPresent判断日期是否是将来或现在日期
@Past检查该字段的日期是在过去
@PastOrPresent判断日期是否是过去或现在日期

其他检查

名称场景
@Email被注释的元素必须是电子邮箱地址
@Pattern(value)被注释的元素必须符合指定的正则表达式

四、@Valid 和 @Validated

@Valid 注解,是 Bean Validation 所定义,可以添加在普通方法、构造方法、方法参数、方法返回、成员变量上,表示它们需要进行约束校验。

@Validated 注解,是 Spring Validation 锁定义,可以添加在类、方法参数、普通方法上,表示它们需要进行约束校验。同时,@Validated 有 value 属性,支持分组校验。

可以把@Validated看作@Valid的增强。

@Validjavax.validation包下) 和 @Validatedorg.springframework.validation.annotation包下)注解。两者大致有以下的区别:

名称是否实现声明式校验是否支持嵌套校验是否支持分组校验
@Validfalsetruefalse
@Validatedtruefalsetrue

绝大多数场景下,我们使用 @Validated 注解即可。而在有嵌套校验的场景,我们使用 @Valid 注解添加到成员属性上。

对于一些简单的校验自己规定统一的就行。

RequestBody参数校验

POST、PUT请求一般会使用requestBody传递参数,这种情况下,后端使用DTO对象进行接收。

只要给DTO对象加上@ValidatedValid注解就能实现自动参数校验。

如果校验失败,会抛出MethodArgumentNotValidException异常,Spring默认会将其转为400(Bad Request)请求。

DTO表示数据传输对象(Data Transfer Object),用于服务器和客户端之间交互传输使用的。在Spring-Web项目中可以表示用于接收请求参数的Bean对象。

如果想查看DTO、VO、POJO....等可以去查看我的另一篇文章java中常用包名解释

  • 登录DOT
1
2
3
4
5
6
7
8
9
10
11
12
13
@Data
public class UserLoginDTO {

private Integer userId;

@NotBlank
@Length(min = 2, max = 10)
private String userName;

@NotBlank(message = "用户密码不能为空")
@Length(min = 6, max = 20)
private String password;
}
  • 登录Controller
1
2
3
4
5
6
7
8
9
@RestController
@RequestMapping("/api/user")
public class UserController{
@RequestMapping("/login")
public R login(@RequestBody @Valid UserLoginDTO){
...业务代码
return R.ok();
}
}
  • 注意事项: 如果前端传到后端的数据为空,那么你就要考虑一下是不是前端的Content-Type类型为application/x-www-form-urlencoded,那么你就需要改为application/json; charset=UTF-8;类型才行。

  • 建议:

    • restful接口:一般将Content-Type设置为application/json; charset=UTF-8;

    • 文件上传:一般Content-Type设置为multipart/form-data;

    • 普通表单提交:一般Content-Type设置为application/x-www-form-urlencoded

requestParam/PathVariable参数校验

GET请求一般会使用requestParam/PathVariable传参。如果参数比较多(比如超过6个),还是推荐使用DTO对象接收。

否则,推荐将一个个参数平铺到方法入参中。在这种情况下,必须在Controller类上标注@Validated注解,并在入参上声明约束注解(如@Min等)。如果校验失败,会抛出ConstraintViolationException异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@RequestMapping("/api/user")
@RestController
@Validated
public class UserController {
// 路径变量
@GetMapping("{userId}")
public Result detail(@PathVariable("userId") @Min(1) Long userId) {
// 校验通过,才会执行业务逻辑处理
UserDTO userDTO = new UserDTO();
userDTO.setUserId(userId);
userDTO.setAccount("11111111111111111");
userDTO.setUserName("xixi");
userDTO.setAccount("11111111111111111");
return Result.ok(userDTO);
}

// 查询参数
@GetMapping("getByName")
public Result getByAccount(@Length(min = 6, max = 20) @NotBlank String userName) {
// 校验通过,才会执行业务逻辑处理
UserDTO userDTO = new UserDTO();
userDTO.setUserId(300);
userDTO.setUserName("hello");
return Result.ok(userDTO);
}
}
// 也可以在每个参数上添加注解 例如
// 查询参数
@GetMapping("getByName")
public Result getByAccount(@Length(min = 6, max = 20) @Validated @NotBlank String userName) {
// 校验通过,才会执行业务逻辑处理
UserDTO userDTO = new UserDTO();
userDTO.setUserId(10000000000000003L);
userDTO.setUserName("hello");
return Result.ok(userDTO);
}

这就是一些参数校验的简单用法。