bboyjing's blog

SpringBoot自定义参数验证

项目中需要进行参数验证是件很平常的事儿,Validation框架也已经帮我们做的很好了。但是,为了更贴近业务需求又或者不想为了验证而去写个Bean,所以还是想自己实现个简单的验证功能。项目基于SpringBoot,具体怎么搭建没啥好说的了,下面挑涉及到的几个点,截取项目中的部分代码来展示下是如何实现的。

全局异常处理

之所以提到全局异常处理,是因为若参数验证失败,抛出IllegalArgumentException来反馈给客户端。

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
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(value = Exception.class)
public void defaultErrorHandler(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, Exception e) throws Exception {
log.error("INTERNAL_SERVER_ERROR", e);
if(e instanceof IllegalArgumentException){
httpServletResponse.setStatus(HttpStatus.BAD_REQUEST.value());
PrintWriter out = httpServletResponse.getWriter();
Result result= Result.error(HttpStatus.BAD_REQUEST.value(), e.getMessage());
ObjectMapper mapper = new ObjectMapper();
out.print(mapper.writeValueAsString(result));
out.flush();
}else{
httpServletResponse.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
PrintWriter out = httpServletResponse.getWriter();
Result result= Result.error(HttpStatus.INTERNAL_SERVER_ERROR.value(),
HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());
ObjectMapper mapper = new ObjectMapper();
out.print(mapper.writeValueAsString(result));
out.flush();
}
}
}

自定义Annotation

自定义Annotation作用于Controller的参数上。

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
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Validation {
/**
* 是否能为空,为true表示不能为空,false表示能够为空
*/
boolean notNull() default false;
/**
* 是否为固定的Integer值
*/
int[] fixedInteger() default {};
/**
* 最小值
* @return
*/
int min() default -1;
/**
* 最大值
* @return
*/
int max() default -1;
}

实现处理逻辑

处理具体的验证逻辑,下面只是示例,具体逻辑自己定。

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
83
84
85
86
87
88
89
90
public class ValidateArgumentResolver implements HandlerMethodArgumentResolver {
/**
* 只需要验证加上@Validation的参数
* @param methodParameter
* @return
*/
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.getParameterAnnotation(Validation.class) != null;
}
@Override
public Object resolveArgument(MethodParameter methodParameter,
ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest,
WebDataBinderFactory webDataBinderFactory) throws Exception {
//获取参数名称
String parameter = methodParameter.getParameterName();
//获取参数类型
String parameterType = methodParameter.getParameterType().getName();
//获取参数值
String value = nativeWebRequest.getParameter(parameter);
//获取参数注解
Validation validation = methodParameter.getParameterAnnotation(Validation.class);
//验证参数合法性,并且返回参数真实类型
return this.validate(validation, parameter, parameterType, value);
}
private Object validate(Validation validation,
String parameter,
String parameterType,
String value) throws IllegalArgumentException{
/**
* 验证参数是否可为空
* 如果不为空,则必须传递该参数,并且参数的值不可为NUll
*/
if(validation.notNull() && StringUtils.isEmpty(value)){
throw new IllegalArgumentException(
String.format("Required parameter '%s' is not present", parameter)
);
}
/**
* 获取参数真实类型的值
*/
Object realValue = value;
if(parameterType.equals("java.lang.Integer")){
realValue = Integer.valueOf(value);
}
/**
* 验证固定的Integer值
*/
if(ArrayUtils.isNotEmpty(validation.fixedInteger())){
if(Arrays.binarySearch(validation.fixedInteger(), (Integer) realValue) < 0){
throw new IllegalArgumentException(
String.format("Parameter '%s' must in %s", parameter,
Arrays.toString(validation.fixedInteger()))
);
}
}
/**
* 验证最小值
*/
if(validation.min() != -1 && (Integer) realValue < validation.min()){
throw new IllegalArgumentException(
String.format("Parameter '%s' must be equal or greater than %s",
parameter, validation.min())
);
}
/**
* 验证最大值
*/
if(validation.max() != -1 && (Integer) realValue > validation.max()){
throw new IllegalArgumentException(
String.format("Parameter '%s' must be equal or less than %s",
parameter, validation.max())
);
}
return realValue;
}
}

Controller中使用Annotation

1
2
3
4
5
6
7
8
9
@RequestMapping(path = "/", method = RequestMethod.POST)
public Result<String> submit(@Validation(notNull = true, fixedInteger = {1,2}) Integer type,
@Validation(notNull = true) String title,
@Validation(notNull = true, min = 1, max = 10000) Integer itemNum,
@Validation(notNull = true) String account,
String excludeTaskStr){
Result result = Result.ok();
return result;
}

随便测试下输出结果:{“code”:400,”msg”:”Required parameter ‘type’ is not present”}
学艺不精,以后有好的方式再改。