Spring Boot中结合Hibernate Validator可以实现优雅的参数校验,而不必在业务代码中写一大堆的参数校验逻辑。Hibernate Validator的基本使用可以参考Spring表单校验,这里介绍一种结合全局异常捕获的方式来实现低耦合简洁的参数校验解决方案。
方法参数校验
新建一个Spring Boot工程,版本为2.1.0.RELEASE,artifactId
为validator
,并引入spring-boot-starter-web
和commons-lang3
依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
项目结构如下所示:
spring-boot-starter-web
已经包含了hibernate-validator
,所以无需单独引入:
在com.example.demo
下新建controller
包,然后创建TestController
,定义一个test1
方法:
@RestController
@Validated
public class TestController {
@GetMapping("test1")
public String test1(
@NotBlank(message = "{required}") String name,
@Email(message = "{invalid}") String email) {
return "success";
}
}
test1
方法的name
参数使用@NotBlank
标注,表示不能为空,提示信息为{required}
占位符里的内容;email
参数使用@Email
注解标注,表示必须为一个合法的邮箱值(可以为空),提示信息为{invalid}
占位符里的内容。要让参数校验生效,我们还需在类上使用@Validated
注解标注。
接下来定义上面两个占位符的内容。在resources目录下新建ValidationMessages.properties文件,内容如下:
required=\u4e0d\u80fd\u4e3a\u7a7a
invalid=\u683c\u5f0f\u4e0d\u5408\u6cd5
内容为中文转Unicode后的值,可以使用http://tool.chinaz.com/tools/unicode.aspx网站转换,\u4e0d\u80fd\u4e3a\u7a7a
转为中文为“不能为空”,\u683c\u5f0f\u4e0d\u5408\u6cd5
转为中文为“格式不合法”。
启动项目,使用Postman进行测试,参数内容如下所示:
这里name
参数值为空,email
参数值为123,访问后,控制台输出异常如下:
可见,使用这种方式参数校验不通过时,会抛出javax.validation.ConstraintViolationException
,我们使用全局异常捕获来处理这种异常:
在com.example.demo
下新建handler
包,然后创建GlobalExceptionHandler
:
@RestControllerAdvice
@Order(value = Ordered.HIGHEST_PRECEDENCE)
public class GlobalExceptionHandler {
/**
* 统一处理请求参数校验(普通传参)
*
* @param e ConstraintViolationException
* @return FebsResponse
*/
@ExceptionHandler(value = ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String handleConstraintViolationException(ConstraintViolationException e) {
StringBuilder message = new StringBuilder();
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
for (ConstraintViolation<?> violation : violations) {
Path path = violation.getPropertyPath();
String[] pathArr = StringUtils.splitByWholeSeparatorPreserveAllTokens(path.toString(), ".");
message.append(pathArr[1]).append(violation.getMessage()).append(",");
}
message = new StringBuilder(message.substring(0, message.length() - 1));
return message.toString();
}
}
上面主要的逻辑是获取校验不通过的参数名称,然后拼接上提示信息,并且HTTP返回状态码为400。重启项目,再次访问刚刚的链接,响应如下所示:
使用实体传参
当参数较少的时候可以使用上面这种方式,但如果参数众多上面的方式就略显繁琐了。这时候我们可以使用实体对象来进行传参。
为了模拟这种情况,我们在com.example.demo
路径下新建domain
包,然后新建User
类:
public class User implements Serializable {
private static final long serialVersionUID = -2731598327208972274L;
@NotBlank(message = "{required}")
private String name;
@Email(message = "{invalid}")
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
接着在TestController
里创建一个test2
方法:
@GetMapping("test2")
public String test2(@Valid User user) {
return "success";
}
使用实体对象传参的方式参数校验需要在相应的参数前加上@Valid
注解。重启项目,再次访问下面这个请求:
控制台会输出如下信息:
Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 2 errors
Field error in object 'user' on field 'name': rejected value []; codes [NotBlank.user.name,NotBlank.name,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.name,name]; arguments []; default message [name]]; default message [不能为空]
Field error in object 'user' on field 'email': rejected value [123]; codes [Email.user.email,Email.email,Email.java.lang.String,Email]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.email,email]; arguments []; default message [email],[Ljavax.validation.constraints.Pattern$Flag;@5fb82092,org.springframework.validation.beanvalidation.SpringValidatorAdapter$ResolvableAttribute@cc0c307]; default message [格式不合法]]
这时候我们需要在GlobalExceptionHandler
捕获org.springframework.validation.BindException
异常:
/**
* 统一处理请求参数校验(实体对象传参)
*
* @param e BindException
* @return FebsResponse
*/
@ExceptionHandler(BindException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String validExceptionHandler(BindException e) {
StringBuilder message = new StringBuilder();
List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
for (FieldError error : fieldErrors) {
message.append(error.getField()).append(error.getDefaultMessage()).append(",");
}
message = new StringBuilder(message.substring(0, message.length() - 1));
return message.toString();
}
重启项目,再次访问刚刚的请求,响应如下所示:
我们将请求参数改为合法的内容:
点击访问,响应如下所示: