0%

SpringMvc请求参数的二次处理

SpringMvc请求参数的二次处理

需求

在一个项目中原来服务端是采用WebService的进行客户端和服务进行交互,现根据需要调整为直接使用HHTP方式访问。仅修改访问的方式,其数据的访问结构等不变。

尝试

1.HandlerMethodArgumentResolver

  1. 原数据结构:在旧项目的设计中,data数据是采用GBKbase64编码,在本次改造中,希望同时将解码的过程也统一处理。
1
2
3
4
5
6
7
8
9
10
11
public class RequestInfo {

private static final long serialVersionUID = -3033941769749731426L;

private String code;

private String message;

@ApiModelProperty("请求数据(basic64)")
private String data;
}
  1. 第一时间想到的就是SpringMvc的参数处理器HandlerMethodArgumentResolver。直接上手开干。
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
// 自定义参数解析器
@Slf4j
public class RequestDateParamMethodArgumentResolver implements HandlerMethodArgumentResolver {
/**
* 是否支持本自定义参数解析器
*
* @Author: xiaocainiaoya
* @Date: 2021/06/21 22:09:57
* @param parameter
* @return:
**/
@Override
public boolean supportsParameter(MethodParameter parameter) {
return true;
}

@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
log.info("hello");
return null;
}
}

// 将参数解析器添加到spring容器中
@Configuration
public class WebConfig implements WebMvcConfigurer {

@Bean
public RequestDateParamMethodArgumentResolver requestDateParamMethodArgumentResolver(){
return new RequestDateParamMethodArgumentResolver();
}

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(requestDateParamMethodArgumentResolver());
}
}

3.本以为可以完美解决,断点打在resolveArgument,正想启动后根据断点查询对应的参数信息,在进一步完善代码。测试发现,根本就没有进入断点。

4.虽说对参数解析器的执行过程不是非常的了解,但是大概知道参数解析器的处理是在适配器Adapter查找对应的HandlerMethod之后执行,所以根据调用栈,查找关键代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
// 所以在内置的很多参数处理器中,只会选择一种进行处理,由于上述的写法中没有自定义注解,
// 尽管supportsParameter方法的返回值为true,用户自定义的参数处理器也是排在内置处理器之后。
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
// 获取到一种匹配的参数处理器之后就跳出了
break;
}
}
}
return result;
}

所以:若需要使用参数处理器进行处理,需要自定义一个注解后,标记在方法的属性处,而改造的本意还是希望业务代码层无需关心解码这一行为,若需要在每个方法处都标记一个注解,与初衷相悖。

2. @InitBinder

这种方式是比较细粒度的控制方式,仅控制单个Controller,在这个Controller中,的参数会被拦截进行数据的二次处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@RestController
@Slf4j
public class ArgumentResolverController {

@InitBinder
public void initBinder(WebDataBinder webDataBinder){
if(webDataBinder.getTarget() instanceof User){
User user = (User)webDataBinder.getTarget();
log.info(user.getUsername());
user.setPassword("updatePassword");
}
}

@RequestMapping("setUserInfo")
public String setUserInfo(@RequestBody User user){
// 这里打印的password是updatePassword,是被修改之后的值, 所以解码可以放在@InitBinder中。
log.info("user: {},{}",user.getUsername(), user.getPassword());
return null;
}
}

3. RequestBodyAdvice

经过尝试ArgumentAdvice可以解决这个问题,通过afterBodyRead方法可以在参数解析器处理之后获取到对应的解析实体,再根据该实体的类型,进行basic64解码。

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
/**
* 请求参数增强处理
*
* @Author: xiaocainiaoya
* @Date: 2021/06/21 22:37:56
**/
@Component
@ControllerAdvice("cn.com.xiaocainiaoya.controller")
public class ArgumentAdvice implements RequestBodyAdvice {

@Override
public boolean supports(MethodParameter methodParameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}

@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) {
if(body instanceof User){
((User) body).setPassword("12312");
}
return body;
}

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

小结

​ 虽然最后没有没有通过参数处理器来解决这个问题,但是在测试过程中也发现了自己对参数解析器的理解不足,原以为只要supportsParameter方法为true,就可以进行参数处理,而我在完善代码时只需要抽象一个顶层属性接口做是否是某种类型的判断即可。

@InitBinderRequestBodyAdvice都是可取的方案,取决于需求是需全局(某个包下的所有Controller)的参数都需要进行某种统一处理,或者是仅仅只需针对某一个Controller进行参数处理。

-------------本文结束感谢您的阅读-------------