Spring之RequestBodyAdvice参数校验

前言

之前写了一篇 自定义starter之接口标准化。其中对requestBody做了validate统一参数校验。最近在写单元测试的时候发现body无法重复消费(正常http接口请求正常), 所以重新写了这部分逻辑。

实现逻辑

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
71
72
73
74
75
76
77
78
79
80
81
82
package cn.idea360.unified;

import org.hibernate.validator.HibernateValidator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import javax.validation.*;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Set;

/**
* 校验请求体参数
*
* @author cuishiying
* @date 2021-01-22
*/
@ConditionalOnProperty(value = "idea360.framework.unified.enable", havingValue = "true")
@RestControllerAdvice
public class ValidateRequestBodyAdvice implements RequestBodyAdvice {

private static final Validator VALIDATOR = Validation.byProvider(HibernateValidator.class).configure()
.failFast(true).buildValidatorFactory().getValidator();

@Override
public boolean supports(MethodParameter methodParameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
// 已添加注解的无需二次处理
boolean matchValidAnnotation = Arrays.stream(methodParameter.getMethodAnnotations())
.anyMatch(t -> t.annotationType().equals(Validated.class) || t.annotationType().equals(Valid.class));
// String不校验
boolean matchString = methodParameter.getParameterType().isAssignableFrom(String.class);
// 匹配则校验
boolean matchPostOrPutMapping = Arrays.stream(methodParameter.getMethodAnnotations()).anyMatch(
t -> t.annotationType().equals(PostMapping.class) || t.annotationType().equals(PutMapping.class));
// 匹配则校验
boolean matchPostOrPutRequestMapping = Arrays.stream(methodParameter.getMethodAnnotations())
.anyMatch(t -> t.annotationType().equals(RequestMapping.class)
&& Arrays.stream(AnnotationUtils.getAnnotation(t, RequestMapping.class).method())
.anyMatch(m -> m.equals(RequestMethod.POST) || m.equals(RequestMethod.PUT)));
return (matchPostOrPutMapping || matchPostOrPutRequestMapping) && !matchValidAnnotation && !matchString;
}

/**
* 此处不做参数校验
*
* String body = IOUtils.toString(inputMessage.getBody(), StandardCharsets.UTF_8);
* Object o = objectMapper.readValue(body, parameter.getParameterType()); InputStream
* inputStreamWrapper = IOUtils.toInputStream(body, StandardCharsets.UTF_8.name());
* return new MappingJacksonInputMessage(inputStreamWrapper,
* inputMessage.getHeaders());
*/
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
return inputMessage;
}

@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
Set<ConstraintViolation<Object>> constraintViolations = VALIDATOR.validate(body);
if (!constraintViolations.isEmpty()) {
throw new ConstraintViolationException(constraintViolations);
}
return body;
}

@Override
public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return body;
}

}

spring.factories修改

1
2
3
4
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.idea360.unified.UnifiedResponseBodyAdvice,\
cn.idea360.unified.UnifiedExceptionHandler,\
cn.idea360.unified.ValidateRequestBodyAdvice

最后

本文到此结束,感谢阅读。如果您觉得不错,请关注公众号【当我遇上你】,您的支持是我写作的最大动力。