SpringBoot解决方案之 参数校验

在以SpringBoot开发Restful接口时, 对于接口的查询参数后台也是要进行校验的,同时还需要给出校验的返回信息放到上文我们统一封装的结构中。那么如何优雅的进行参数的统一校验呢?

什么是不优雅的参数校验

后端对前端传过来的参数也是需要进行校验的,如果在 controller 中直接校验需要用大量的 if else 做判断

以添加用户的接口为例,需要对前端传过来的参数进行校验, 如下的校验就是不优雅的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RestController
@RequestMapping("/user")
public class UserController {

@PostMapping("add")
public R add(User user) {
if(user.getName()==null) {
return R.error("user name should not be empty");
} else if(user.getName().length()<5 || user.getName().length()>50){
return R.error("user name length should between 5-50");
}
if(user.getAge()< 1 || user.getAge()> 150) {
return R.error("invalid age");
}
// ...
return R.ok();
}
}

针对这个普遍的问题,Java开发者在Java API规范 (JSR303) 定义了Bean校验的标准validation-api,但没有提供实现。

hibernate validation 是对这个规范的实现,并增加了校验注解如@Email、@Length等。

Spring Validation 是对 hibernate validation 的二次封装,用于支持spring mvc参数自动校验。

接下来,我们以springboot项目为例,介绍Spring Validation的使用

实现案例

本例子采用 spring validation 对参数绑定进行校验,主要给你提供参数校验的思路。

POM

添加pom依赖

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

请求参数封装

单一职责,所以将查询用户的参数封装到 UserParam 中, 而不是 User(数据库实体)本身。

对每个参数字段添加 validation 注解约束和 message。

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
@Data
@Builder
@ApiModel(value = "User", subTypes = {AddressParam.class})
public class UserParam implements Serializable {

private static final long serialVersionUID = 1L;

@NotEmpty(message = "could not be empty")
private String userId;

@NotEmpty(message = "could not be empty")
@Email(message = "invalid email")
private String email;

@NotEmpty(message = "could not be empty")
@Pattern(regexp = "^(\\d{6})(\\d{4})(\\d{2})(\\d{2})(\\d{3})([0-9]|X)$", message = "invalid ID")
private String cardNo;

@NotEmpty(message = "could not be empty")
@Length(min = 1, max = 10, message = "nick name should be 1-10")
private String nickName;

@NotEmpty(message = "could not be empty")
@Range(min = 0, max = 1, message = "sex should be 0-1")
private int sex;

@Max(value = 100, message = "Please input valid age")
private int age;

@Valid
private AddressParam address;

}

Controller中获取参数绑定结果

使用 @Valid 或者 @Validated 注解,参数校验的值放在 BindingResult 中

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
@Slf4j
@Api(value = "User Interfaces", tags = "User Interfaces")
@RestController
@RequestMapping("/user")
public class UserController {

/**
* http://localhost:8080/user/add .
*
* @param userParam user param
* @return user
*/
@ApiOperation("Add User")
@ApiImplicitParam(name = "userParam", type = "body", dataTypeClass = UserParam.class, required = true)
@PostMapping("add")
public ResponseEntity<String> add(@Valid @RequestBody UserParam userParam, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
List<ObjectError> errors = bindingResult.getAllErrors();
errors.forEach(p -> {
FieldError fieldError = (FieldError) p;
log.error("Invalid Parameter : object - {},field - {},errorMessage - {}", fieldError.getObjectName(), fieldError.getField(), fieldError.getDefaultMessage());
});
return R.error.message("invalid parameter");
}
return R.ok("success");
}
}