Spring Boot 参数校验
Wenhao Wang 2020-07-20 Spring Boot
Web项目中优雅处理前端传递的参数,使用注解完成参数的校验
# 1. 添加注解
在需要被校验的参数Bean上添加注解@Validated或@Valid
@Validated 是Spring对@Valid进行的包装
1在参数Bean上添加校验注解
对于非Bean参数,可以在参数前面加校验注解,并在Controller上添加注解@Validated
# 2. 优雅处理结果
# 方式1:手动校验参数
创建参数校验工具类
package com.leng.urlconversion.common; import com.google.common.collect.Sets; import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; /** * @author wangwenhao * @description 实体类校验 * @date 2020-04-21 17:14 */ public class BeanValidation<T> { private static final Validator VALIDATOR; static{ VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator(); } /** * 参数校验 * @param bean 参数 * @param groups * @param <T> 泛型 */ public static <T> void doValid(T bean, Class<?>... groups) { Set<ConstraintViolation<T>> violations = VALIDATOR.validate(bean, groups); if (!violations.isEmpty()) { // strategy1(violations); strategy2(violations); } } /** * 策略2 返回校验详细结果 * @param violations * @param <T> */ private static <T> void strategy2(Set<ConstraintViolation<T>> violations) { HashSet<String> message = Sets.newHashSet(); message.addAll(violations.stream().map(violation -> String.format("%s='%s': %s", violation.getPropertyPath(), violation.getInvalidValue(), violation.getMessage())).collect(Collectors.toList())); System.out.println(message); // TODO 使用统一响应对象返回 } /** * 策略1 仅返回提示信息 * @param violations * @param <T> */ private static <T> void strategy1(Set<ConstraintViolation<T>> violations) { StringBuffer buffer = new StringBuffer(); violations.forEach(validation -> buffer.append(validation.getMessage()).append("\t")); System.out.println(buffer.toString()); // TODO 使用统一响应对象返回 } }
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62使用手动校验参数时,无需在需要校验的参数Bean前加注解
手动校验参数只能校验Bean参数
在需要校验参数的地方调用工具类方法
# 方式2:使用统一异常处理
参数校验失败会抛出
BindException
,对该异常添加处理逻辑即可全局异常处理方式1:
package com.leng.urlconversion.common; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.springframework.validation.BindException; import org.springframework.validation.BindingResult; import org.springframework.validation.FieldError; import org.springframework.validation.ObjectError; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.validation.ConstraintViolationException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.stream.Collectors; /** * @author wangwenhao * @description 全局异常处理 * @date 2020-04-22 10:25 */ @RestControllerAdvice public class CustomExceptionHandler { @ExceptionHandler(Exception.class) public Result dealException(Exception exception) { if (exception instanceof BindException) { // TODO 此处进行参数校验异常处理 return Result.fail("BindException"); } if (exception instanceof ConstraintViolationException) { System.out.println("ConstraintViolationException"); return Result.fail("ConstraintViolationException"); } // ... 可以添加其他类型异常处理逻辑 return Result.fail("未知异常"); } }
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
37
38
39
40
41全局异常处理方式2:
package com.leng.urlconversion.common; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.springframework.validation.BindException; import org.springframework.validation.BindingResult; import org.springframework.validation.FieldError; import org.springframework.validation.ObjectError; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.validation.ConstraintViolationException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.stream.Collectors; /** * @author wangwenhao * @description 全局异常处理 * @date 2020-04-22 10:25 */ @RestControllerAdvice public class CustomExceptionHandler { @ExceptionHandler(BindException.class) public Result dealBindExceptionHandler(BindException bindException) { if (bindException.hasFieldErrors()) { List<FieldError> fieldErrors = bindException.getFieldErrors(); // 返回方式1:以List集合存放错误信息返回 // ArrayList<String> messageList = Lists.newArrayList(); // messageList.addAll(fieldErrors.stream().map( error -> // String.format("%s = '%s' : %s", error.getField(), error.getRejectedValue(), error.getDefaultMessage()) // ).collect(Collectors.toList())); // return Result.fail(messageList.toString()); // 返回方式2:以String拼接错误信息返回 String result = fieldErrors.stream().map(error -> String.format("%s = '%s' : %s", error.getField(), error.getRejectedValue(), error.getDefaultMessage()) ).collect(Collectors.joining(";\t")); return Result.fail(result); } return Result.fail("其他异常---" + bindException.getObjectName()); } }
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
37
38
39
40
41
42
43
44
45
46
47全局异常处理方式3:
package com.leng.urlconversion.common; import org.springframework.validation.BindException; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; /** * @author wangwenhao * @description 全局异常处理 * @date 2020-04-22 10:25 */ @RestControllerAdvice public class CustomExceptionHandler { @ExceptionHandler(Exception.class) public Result dealException(Exception exception) { return Optional.ofNullable(dealValidException(exception)).orElse(Result.fail("未知异常-")); } private Result dealValidException(Exception exception) { // 当请求方式为Get,并且使用Bean接收参数校验失败会抛出该异常 if (exception instanceof BindException && ((BindException)exception).hasFieldErrors()) { List<FieldError> fieldErrors = ((BindException)exception).getFieldErrors(); // ArrayList<String> messageList = Lists.newArrayList(); // messageList.addAll(fieldErrors.stream().map( error -> // String.format("%s = '%s' : %s", error.getField(), error.getRejectedValue(), error.getDefaultMessage()) // ).collect(Collectors.toList())); // return Result.fail(messageList.toString()); String result = fieldErrors.stream().map(error -> String.format("%s = '%s' : %s", error.getField(), error.getRejectedValue(), error.getDefaultMessage()) ).collect(Collectors.joining(";\t")); return Result.fail(result); } // 当请求方式为Get,未使用Bean接收参数校验失败会抛出该异常 if (exception instanceof ConstraintViolationException) { Set<ConstraintViolation<?>> violationSet = ((ConstraintViolationException) exception).getConstraintViolations(); String result = violationSet.stream().map(error -> String.format("%s = '%s' : %s", error.getPropertyPath(), error.getInvalidValue(), error.getMessage()) ).collect(Collectors.joining(";\t")); return Result.fail(result); } // 当请求方式为Post,使用@RequestBody 接收参数校验失败会抛出该异常 if (exception instanceof MethodArgumentNotValidException) { List<FieldError> fieldErrors = ((MethodArgumentNotValidException) exception).getBindingResult().getFieldErrors(); // ArrayList<String> messageList = Lists.newArrayList(); // messageList.addAll(fieldErrors.stream().map( error -> // String.format("%s = '%s' : %s", error.getField(), error.getRejectedValue(), error.getDefaultMessage()) // ).collect(Collectors.toList())); // return Result.fail(messageList.toString()); String result = fieldErrors.stream().map(error -> String.format("%s = '%s' : %s", error.getField(), error.getRejectedValue(), error.getDefaultMessage()) ).collect(Collectors.joining(";\t")); return Result.fail(result); } return null; } }
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# 3. 配置校验类,实现==快速失败==
package com.leng.urlconversion.config;
import org.hibernate.validator.HibernateValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
/**
* @author wangwenhao
* @description 配置校验类
* @date 2020-04-23 15:46
*/
@Configuration
public class ValidationConfig {
@Bean
public Validator validator() {
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
.configure()
//failFast的意思只要出现校验失败的情况,就立即结束校验,不再进行后续的校验。
.failFast(true)
.buildValidatorFactory();
return validatorFactory.getValidator();
}
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor(Validator validator) {
MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor();
methodValidationPostProcessor.setValidator(validator);
return methodValidationPostProcessor;
}
}
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
37
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
37
# 3.常用校验注解
# 1.Bean Validation 中内置的 constraint
@Valid | 被注释的元素是一个对象,需要检查此对象的所有字段值 |
---|---|
@Null | 被注释的元素必须为 null |
@NotNull | 被注释的元素必须不为 null |
@AssertTrue | 被注释的元素必须为 true |
@AssertFalse | 被注释的元素必须为 false |
@Min(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@Max(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@DecimalMin(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@DecimalMax(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@Size(max, min) | 被注释的元素的大小必须在指定的范围内 |
@Digits (integer, fraction) | 被注释的元素必须是一个数字,其值必须在可接受的范围内 |
@Past | 被注释的元素必须是一个过去的日期 |
@Future | 被注释的元素必须是一个将来的日期 |
@Pattern(value) | 被注释的元素必须符合指定的正则表达式 |
# 2.Hibernate Validator 附加的 constraint
注解 | 作用 |
---|---|
被注释的元素必须是电子邮箱地址 | |
@Length(min=, max=) | 被注释的字符串的大小必须在指定的范围内 |
@NotEmpty | 被注释的字符串的必须非空 |
@Range(min=, max=) | 被注释的元素必须在合适的范围内 |
@NotBlank | 被注释的字符串的必须非空 |
@URL(protocol=, host=, port=, regexp=, flags=) | 被注释的字符串必须是一个有效的url |
@CreditCardNumber | 被注释的字符串必须通过Luhn校验算法, 银行卡,信用卡等号码一般都用Luhn 计算合法性 |
@ScriptAssert (lang=, script=, alias=) | 要有Java Scripting API 即JSR 223 ("Scripting for the JavaTM Platform")的实现 |
@SafeHtml (whitelistType=, additionalTags=) | classpath中要有jsoup包 |
- @NotNull 任何对象的value不能为null
- @NotEmpty 集合对象的元素不为0,即集合不为空,也可以用于字符串不为null
- @NotBlank 只能用于字符串不为null,并且字符串trim()以后length要大于0